mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Adds SQL Binding Create Azure Function with SQL Binding tests (#19538)
* add a couple of azure function service tests * address comments * use mock for azureFunctionExtensionAPI
This commit is contained in:
@@ -458,7 +458,6 @@ export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const listOfConnectionStringMethods = [constants.connectionProfile, constants.userConnectionString];
|
|
||||||
let selectedConnectionStringMethod: string | undefined;
|
let selectedConnectionStringMethod: string | undefined;
|
||||||
let connectionString: string | undefined = '';
|
let connectionString: string | undefined = '';
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -467,6 +466,7 @@ export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.
|
|||||||
const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName);
|
const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName);
|
||||||
|
|
||||||
if (!connectionInfo) {
|
if (!connectionInfo) {
|
||||||
|
const listOfConnectionStringMethods = [constants.connectionProfile, constants.userConnectionString];
|
||||||
// show the connection string methods (user input and connection profile options)
|
// show the connection string methods (user input and connection profile options)
|
||||||
selectedConnectionStringMethod = await vscode.window.showQuickPick(listOfConnectionStringMethods, {
|
selectedConnectionStringMethod = await vscode.window.showQuickPick(listOfConnectionStringMethods, {
|
||||||
canPickMany: false,
|
canPickMany: false,
|
||||||
@@ -509,7 +509,6 @@ export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.
|
|||||||
// user cancelled the prompts
|
// user cancelled the prompts
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const success = await setLocalAppSetting(projectFolder, newConnectionStringSettingName, connectionString);
|
const success = await setLocalAppSetting(projectFolder, newConnectionStringSettingName, connectionString);
|
||||||
if (success) {
|
if (success) {
|
||||||
// exit both loops and insert binding
|
// exit both loops and insert binding
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export async function createAzureFunction(node?: ITreeNodeInfo): Promise<void> {
|
|||||||
|
|
||||||
isCreateNewProject = true;
|
isCreateNewProject = true;
|
||||||
telemetryStep = CreateAzureFunctionStep.getSelectedFolder;
|
telemetryStep = CreateAzureFunctionStep.getSelectedFolder;
|
||||||
// user either has not folder open or an empty workspace
|
// user either has no folder open or an empty workspace
|
||||||
// prompt user to choose a folder to create the project in
|
// prompt user to choose a folder to create the project in
|
||||||
const browseProjectLocation = await vscode.window.showQuickPick(
|
const browseProjectLocation = await vscode.window.showQuickPick(
|
||||||
[constants.browseEllipsisWithIcon],
|
[constants.browseEllipsisWithIcon],
|
||||||
@@ -190,6 +190,7 @@ export async function createAzureFunction(node?: ITreeNodeInfo): Promise<void> {
|
|||||||
// prompt for Connection String Setting Name
|
// prompt for Connection String Setting Name
|
||||||
let connectionStringInfo: IConnectionStringInfo | undefined = { connectionStringSettingName: constants.sqlConnectionStringSetting, connectionInfo: connectionInfo };
|
let connectionStringInfo: IConnectionStringInfo | undefined = { connectionStringSettingName: constants.sqlConnectionStringSetting, connectionInfo: connectionInfo };
|
||||||
if (!isCreateNewProject && projectFile) {
|
if (!isCreateNewProject && projectFile) {
|
||||||
|
// if it is not a new project, we can prompt user for connection string setting name and connection string password prompts
|
||||||
telemetryStep = CreateAzureFunctionStep.getConnectionStringSettingName;
|
telemetryStep = CreateAzureFunctionStep.getConnectionStringSettingName;
|
||||||
connectionStringInfo = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(vscode.Uri.parse(projectFile), connectionInfo);
|
connectionStringInfo = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(vscode.Uri.parse(projectFile), connectionInfo);
|
||||||
if (!connectionStringInfo) {
|
if (!connectionStringInfo) {
|
||||||
@@ -227,7 +228,6 @@ export async function createAzureFunction(node?: ITreeNodeInfo): Promise<void> {
|
|||||||
// check for the new function file to be created and dispose of the file system watcher
|
// check for the new function file to be created and dispose of the file system watcher
|
||||||
const timeoutForFunctionFile = utils.timeoutPromise(constants.timeoutAzureFunctionFileError);
|
const timeoutForFunctionFile = utils.timeoutPromise(constants.timeoutAzureFunctionFileError);
|
||||||
await Promise.race([newFunctionFileObject.filePromise, timeoutForFunctionFile]);
|
await Promise.race([newFunctionFileObject.filePromise, timeoutForFunctionFile]);
|
||||||
|
|
||||||
telemetryStep = 'finishCreateFunction';
|
telemetryStep = 'finishCreateFunction';
|
||||||
propertyBag.telemetryStep = telemetryStep;
|
propertyBag.telemetryStep = telemetryStep;
|
||||||
exitReason = ExitReason.finishCreate;
|
exitReason = ExitReason.finishCreate;
|
||||||
@@ -254,9 +254,7 @@ export async function createAzureFunction(node?: ITreeNodeInfo): Promise<void> {
|
|||||||
propertyBag.exitReason = exitReason;
|
propertyBag.exitReason = exitReason;
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick)
|
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick)
|
||||||
.withAdditionalProperties(propertyBag).send();
|
.withAdditionalProperties(propertyBag).send();
|
||||||
if (newFunctionFileObject) {
|
newFunctionFileObject?.watcherDisposable.dispose();
|
||||||
newFunctionFileObject.watcherDisposable.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ const rootFolderPath = 'test';
|
|||||||
const localSettingsPath: string = path.join(rootFolderPath, 'local.settings.json');
|
const localSettingsPath: string = path.join(rootFolderPath, 'local.settings.json');
|
||||||
let testUtils: TestUtils;
|
let testUtils: TestUtils;
|
||||||
|
|
||||||
describe('Tests to verify Azure Functions Utils functions', function (): void {
|
describe('AzureFunctionUtils', function (): void {
|
||||||
beforeEach(function (): void {
|
beforeEach(function (): void {
|
||||||
testUtils = createTestUtils();
|
testUtils = createTestUtils();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Local.Settings.Json', function (): void {
|
||||||
it('Should correctly parse local.settings.json', async () => {
|
it('Should correctly parse local.settings.json', async () => {
|
||||||
sinon.stub(fs, 'existsSync').withArgs(localSettingsPath).returns(true);
|
sinon.stub(fs, 'existsSync').withArgs(localSettingsPath).returns(true);
|
||||||
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
|
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
|
||||||
@@ -77,8 +78,9 @@ describe('Tests to verify Azure Functions Utils functions', function (): void {
|
|||||||
await azureFunctionsUtils.addConnectionStringToConfig(connectionString, rootFolderPath);
|
await azureFunctionsUtils.addConnectionStringToConfig(connectionString, rootFolderPath);
|
||||||
should(writeFileStub.calledWithExactly(localSettingsPath, `{\n "IsEncrypted": false,\n "Values": {\n "test1": "test1",\n "test2": "test2",\n "test3": "test3",\n "SqlConnectionString": "testConnectionString"\n }\n}`)).equals(true);
|
should(writeFileStub.calledWithExactly(localSettingsPath, `{\n "IsEncrypted": false,\n "Values": {\n "test1": "test1",\n "test2": "test2",\n "test3": "test3",\n "SqlConnectionString": "testConnectionString"\n }\n}`)).equals(true);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Password Prompts
|
describe('Password Prompts', function (): void {
|
||||||
it('Should include password if user includes password and connection info contains the password and auth type is SQL', async () => {
|
it('Should include password if user includes password and connection info contains the password and auth type is SQL', async () => {
|
||||||
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
let connectionInfo: IConnectionInfo = createTestCredentials();// Mocks promptForConnection
|
let connectionInfo: IConnectionInfo = createTestCredentials();// Mocks promptForConnection
|
||||||
@@ -232,6 +234,7 @@ describe('Tests to verify Azure Functions Utils functions', function (): void {
|
|||||||
// returned connection string should have the entered password
|
// returned connection string should have the entered password
|
||||||
should(getConnectionString).equals(`Server=${connectionInfo.server};Initial Catalog=${connectionInfo.database};User ID=${connectionInfo.user};Password=${constants.passwordPlaceholder};`);
|
should(getConnectionString).equals(`Server=${connectionInfo.server};Initial Catalog=${connectionInfo.database};User ID=${connectionInfo.user};Password=${constants.passwordPlaceholder};`);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(async function (): Promise<void> {
|
afterEach(async function (): Promise<void> {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
|
|||||||
@@ -0,0 +1,201 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sinon from 'sinon';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as TypeMoq from 'typemoq';
|
||||||
|
import * as utils from '../../common/utils';
|
||||||
|
import * as constants from '../../common/constants';
|
||||||
|
import * as azureFunctionUtils from '../../common/azureFunctionsUtils';
|
||||||
|
import * as azureFunctionsContracts from '../../contracts/azureFunctions/azureFunctionsContracts';
|
||||||
|
import * as azureFunctionService from '../../services/azureFunctionsService';
|
||||||
|
|
||||||
|
import { createTestUtils, TestUtils, createTestCredentials, createTestTableNode } from '../testUtils';
|
||||||
|
import { IConnectionInfo } from 'vscode-mssql';
|
||||||
|
import { BindingType } from 'sql-bindings';
|
||||||
|
|
||||||
|
const rootFolderPath = 'test';
|
||||||
|
const projectFilePath: string = path.join(rootFolderPath, 'test.csproj');
|
||||||
|
let testUtils: TestUtils;
|
||||||
|
describe('AzureFunctionsService', () => {
|
||||||
|
describe('Create Azure Function with SQL Binding', () => {
|
||||||
|
beforeEach(function (): void {
|
||||||
|
testUtils = createTestUtils();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function (): void {
|
||||||
|
sinon.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should show info message to install azure functions extension if not installed', async function (): Promise<void> {
|
||||||
|
const infoStub = sinon.stub(vscode.window, 'showInformationMessage').resolves(undefined);
|
||||||
|
await azureFunctionService.createAzureFunction();
|
||||||
|
should(infoStub.calledOnce).be.true('showInformationMessage should be called once');
|
||||||
|
should(infoStub.args).containEql([constants.azureFunctionsExtensionNotFound,
|
||||||
|
constants.install, constants.learnMore, constants.doNotInstall]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should create azure function project using the command from command palette (no connection info)', async function (): Promise<void> {
|
||||||
|
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials();// Mocks promptForConnection
|
||||||
|
connectionInfo.database = 'testDb';
|
||||||
|
|
||||||
|
let connectionDetails = { options: connectionInfo };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
const spy = sinon.spy(vscode.window, 'showErrorMessage');
|
||||||
|
|
||||||
|
// select input or output binding
|
||||||
|
let quickpickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(<any>{ label: constants.input, type: BindingType.input });
|
||||||
|
|
||||||
|
// no table used for connection info so prompt user to get connection info
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.promptForConnection(true)).returns(() => Promise.resolve(connectionInfo));
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.listDatabases('testConnectionURI')).returns(() => Promise.resolve(['testDb']));
|
||||||
|
// select the testDB from list of databases based on connection info
|
||||||
|
quickpickStub.onSecondCall().returns(Promise.resolve('testDb') as any);
|
||||||
|
// get tables from selected database
|
||||||
|
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionUtils.tablesQuery('testDb') };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params))
|
||||||
|
.returns(() => Promise.resolve({ rowCount: 1, columnInfo: [], rows: [['[schema].[testTable]']] }));
|
||||||
|
// select the schema.testTable from list of tables based on connection info and database
|
||||||
|
quickpickStub.onThirdCall().returns(Promise.resolve('[schema].[testTable]') as any);
|
||||||
|
|
||||||
|
// set azure function name
|
||||||
|
let inputStub = sinon.stub(vscode.window, 'showInputBox').resolves('testFunctionName');
|
||||||
|
|
||||||
|
// promptAndUpdateConnectionStringSetting
|
||||||
|
quickpickStub.onCall(3).resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
||||||
|
inputStub.onSecondCall().resolves('SqlConnectionString');
|
||||||
|
// promptConnectionStringPasswordAndUpdateConnectionString - tested in AzureFunctionUtils.test.ts
|
||||||
|
quickpickStub.onCall(4).returns(Promise.resolve(constants.yesString) as any);
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
// setLocalAppSetting with connection string setting name and connection string
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile');
|
||||||
|
sinon.stub(azureFunctionUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'SqlConnectionString', 'testConnectionString').returns(Promise.resolve(true));
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
const testWatcher = TypeMoq.Mock.ofType<vscode.FileSystemWatcher>().object;
|
||||||
|
sinon.stub(azureFunctionUtils, 'waitForNewFunctionFile').withArgs(sinon.match.any).returns({ filePromise: Promise.resolve('TestFileCreated'), watcherDisposable: testWatcher });
|
||||||
|
await azureFunctionService.createAzureFunction();
|
||||||
|
|
||||||
|
should(spy.notCalled).be.true('showErrorMessage should not have been called');
|
||||||
|
// set the connection info to be the one the user selects from list of databases quickpick
|
||||||
|
should(connectionInfo.database).equal('testDb');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should create azure function project using command via the sql server table OE', async function (): Promise<void> {
|
||||||
|
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials();// Mocks promptForConnection
|
||||||
|
let connectionDetails = { options: connectionInfo };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
const spy = sinon.spy(vscode.window, 'showErrorMessage');
|
||||||
|
|
||||||
|
// select input or output binding
|
||||||
|
let quickpickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(<any>{ label: constants.input, type: BindingType.input });
|
||||||
|
// table node used when creating azure function project
|
||||||
|
let tableTestNode = createTestTableNode(connectionInfo);
|
||||||
|
|
||||||
|
// set azure function name
|
||||||
|
let inputStub = sinon.stub(vscode.window, 'showInputBox').resolves('testFunctionName');
|
||||||
|
|
||||||
|
// promptAndUpdateConnectionStringSetting
|
||||||
|
quickpickStub.onSecondCall().resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
||||||
|
inputStub.onSecondCall().resolves('SqlConnectionString');
|
||||||
|
// promptConnectionStringPasswordAndUpdateConnectionString - tested in AzureFunctionUtils.test.ts
|
||||||
|
quickpickStub.onThirdCall().returns(Promise.resolve(constants.yesString) as any);
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
// setLocalAppSetting with connection string setting name and connection string
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile');
|
||||||
|
sinon.stub(azureFunctionUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'SqlConnectionString', 'testConnectionString').returns(Promise.resolve(true));
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
const testWatcher = TypeMoq.Mock.ofType<vscode.FileSystemWatcher>().object;
|
||||||
|
sinon.stub(azureFunctionUtils, 'waitForNewFunctionFile').withArgs(sinon.match.any).returns({ filePromise: Promise.resolve('TestFileCreated'), watcherDisposable: testWatcher });
|
||||||
|
|
||||||
|
await azureFunctionService.createAzureFunction(tableTestNode);
|
||||||
|
|
||||||
|
should(spy.notCalled).be.true('showErrorMessage should not have been called');
|
||||||
|
// set the connection info to be the one used from the test table node from OE
|
||||||
|
should(connectionInfo.database).equal('testDb');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should open link to learn more about SQL bindings when no azure function project found in folder or workspace', async function (): Promise<void> {
|
||||||
|
// This test will ask user that an azure function project must be opened to create an azure function with sql binding
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials();// Mocks promptForConnection
|
||||||
|
let connectionDetails = { options: connectionInfo };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
const executeCommandSpy = sinon.stub(vscode.commands, 'executeCommand').withArgs(sinon.match.any, sinon.match.any).returns(Promise.resolve());
|
||||||
|
const showErrorStub = sinon.stub(vscode.window, 'showErrorMessage').returns(Promise.resolve(constants.learnMore) as any);
|
||||||
|
await azureFunctionService.createAzureFunction();
|
||||||
|
|
||||||
|
should(executeCommandSpy.calledOnce).be.true('showErrorMessage should have been called');
|
||||||
|
should(showErrorStub.calledOnce).be.true('showErrorMessage should have been called');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should ask the user to choose a folder to use for the azure project and create an azure function with the selected folder', async function (): Promise<void> {
|
||||||
|
// This test will ask user that an azure function project must be opened to create an azure function with sql binding
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials();// Mocks promptForConnection
|
||||||
|
let connectionDetails = { options: connectionInfo };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
// error since no project file found in workspace or folder
|
||||||
|
const showErrorStub = sinon.stub(vscode.window, 'showErrorMessage').returns(Promise.resolve(constants.createProject) as any);
|
||||||
|
|
||||||
|
// user chooses to browse for folder
|
||||||
|
let quickpickStub = sinon.stub(vscode.window, 'showQuickPick').returns(Promise.resolve(constants.browseEllipsisWithIcon) as any);
|
||||||
|
|
||||||
|
// stub out folder to be chosen (showOpenDialog)
|
||||||
|
sinon.stub(vscode.window, 'showOpenDialog').withArgs(sinon.match.any).resolves([vscode.Uri.file(projectFilePath)]);
|
||||||
|
// select input or output binding
|
||||||
|
quickpickStub.onSecondCall().resolves(<any>{ label: constants.input, type: BindingType.input });
|
||||||
|
// table node used when creating azure function project
|
||||||
|
let tableTestNode = createTestTableNode(connectionInfo);
|
||||||
|
|
||||||
|
// set azure function name
|
||||||
|
let inputStub = sinon.stub(vscode.window, 'showInputBox').resolves('testFunctionName');
|
||||||
|
|
||||||
|
// promptAndUpdateConnectionStringSetting
|
||||||
|
quickpickStub.onThirdCall().resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
||||||
|
inputStub.onSecondCall().resolves('SqlConnectionString');
|
||||||
|
// promptConnectionStringPasswordAndUpdateConnectionString - tested in AzureFunctionUtils.test.ts
|
||||||
|
quickpickStub.onCall(3).returns(Promise.resolve(constants.yesString) as any);
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
// setLocalAppSetting with connection string setting name and connection string
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile');
|
||||||
|
sinon.stub(azureFunctionUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'SqlConnectionString', 'testConnectionString').returns(Promise.resolve(true));
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
const testWatcher = TypeMoq.Mock.ofType<vscode.FileSystemWatcher>().object;
|
||||||
|
sinon.stub(azureFunctionUtils, 'waitForNewFunctionFile').withArgs(sinon.match.any).returns({ filePromise: Promise.resolve('TestFileCreated'), watcherDisposable: testWatcher });
|
||||||
|
|
||||||
|
await azureFunctionService.createAzureFunction(tableTestNode);
|
||||||
|
|
||||||
|
should(showErrorStub.calledOnce).be.true('showErrorMessage should have been called');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -8,6 +8,7 @@ import * as TypeMoq from 'typemoq';
|
|||||||
import * as mssql from 'mssql';
|
import * as mssql from 'mssql';
|
||||||
import * as vscodeMssql from 'vscode-mssql';
|
import * as vscodeMssql from 'vscode-mssql';
|
||||||
import { RequestType } from 'vscode-languageclient';
|
import { RequestType } from 'vscode-languageclient';
|
||||||
|
import { AzureFunctionsExtensionApi } from '../../../types/vscode-azurefunctions.api';
|
||||||
|
|
||||||
export interface TestUtils {
|
export interface TestUtils {
|
||||||
context: vscode.ExtensionContext;
|
context: vscode.ExtensionContext;
|
||||||
@@ -16,6 +17,7 @@ export interface TestUtils {
|
|||||||
vscodeMssqlIExtension: TypeMoq.IMock<vscodeMssql.IExtension>
|
vscodeMssqlIExtension: TypeMoq.IMock<vscodeMssql.IExtension>
|
||||||
dacFxMssqlService: TypeMoq.IMock<vscodeMssql.IDacFxService>;
|
dacFxMssqlService: TypeMoq.IMock<vscodeMssql.IDacFxService>;
|
||||||
schemaCompareService: TypeMoq.IMock<vscodeMssql.ISchemaCompareService>;
|
schemaCompareService: TypeMoq.IMock<vscodeMssql.ISchemaCompareService>;
|
||||||
|
azureFunctionsExtensionApi: TypeMoq.IMock<AzureFunctionsExtensionApi>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MockVscodeMssqlIExtension implements vscodeMssql.IExtension {
|
export class MockVscodeMssqlIExtension implements vscodeMssql.IExtension {
|
||||||
@@ -59,13 +61,17 @@ export class MockVscodeMssqlIExtension implements vscodeMssql.IExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createTestUtils(): TestUtils {
|
export function createTestUtils(): TestUtils {
|
||||||
|
// Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
||||||
|
const azureFunctionsExtensionApi = TypeMoq.Mock.ofType<AzureFunctionsExtensionApi>();
|
||||||
|
azureFunctionsExtensionApi.setup((x: any) => x.then).returns(() => undefined);
|
||||||
return {
|
return {
|
||||||
context: TypeMoq.Mock.ofType<vscode.ExtensionContext>().object,
|
context: TypeMoq.Mock.ofType<vscode.ExtensionContext>().object,
|
||||||
dacFxService: TypeMoq.Mock.ofType<mssql.IDacFxService>(),
|
dacFxService: TypeMoq.Mock.ofType<mssql.IDacFxService>(),
|
||||||
vscodeMssqlIExtension: TypeMoq.Mock.ofType(MockVscodeMssqlIExtension),
|
vscodeMssqlIExtension: TypeMoq.Mock.ofType(MockVscodeMssqlIExtension),
|
||||||
dacFxMssqlService: TypeMoq.Mock.ofType<vscodeMssql.IDacFxService>(),
|
dacFxMssqlService: TypeMoq.Mock.ofType<vscodeMssql.IDacFxService>(),
|
||||||
schemaCompareService: TypeMoq.Mock.ofType<vscodeMssql.ISchemaCompareService>(),
|
schemaCompareService: TypeMoq.Mock.ofType<vscodeMssql.ISchemaCompareService>(),
|
||||||
outputChannel: TypeMoq.Mock.ofType<vscode.OutputChannel>().object
|
outputChannel: TypeMoq.Mock.ofType<vscode.OutputChannel>().object,
|
||||||
|
azureFunctionsExtensionApi
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,3 +113,39 @@ export function createTestCredentials(): vscodeMssql.IConnectionInfo {
|
|||||||
};
|
};
|
||||||
return creds;
|
return creds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create SQL server table node used for testing
|
||||||
|
* @param connectionInfo the connection info used for the test case
|
||||||
|
* @returns SQL Server table node
|
||||||
|
*/
|
||||||
|
export function createTestTableNode(connectionInfo: vscodeMssql.IConnectionInfo): vscodeMssql.ITreeNodeInfo {
|
||||||
|
return {
|
||||||
|
connectionInfo: connectionInfo,
|
||||||
|
nodeType: 'Table',
|
||||||
|
metadata: {
|
||||||
|
metadataType: 0,
|
||||||
|
metadataTypeName: 'Table',
|
||||||
|
urn: '',
|
||||||
|
name: 'testTable',
|
||||||
|
schema: 'testSchema',
|
||||||
|
},
|
||||||
|
parentNode: {
|
||||||
|
connectionInfo: connectionInfo,
|
||||||
|
nodeType: 'Folder',
|
||||||
|
metadata: null!,
|
||||||
|
parentNode: {
|
||||||
|
connectionInfo: connectionInfo,
|
||||||
|
nodeType: 'Database',
|
||||||
|
metadata: {
|
||||||
|
metadataType: 0,
|
||||||
|
metadataTypeName: 'Database',
|
||||||
|
urn: '',
|
||||||
|
name: 'testDb',
|
||||||
|
schema: null!,
|
||||||
|
},
|
||||||
|
parentNode: undefined! // set to undefined since we do not need further parent node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user