Add SQL Bindings Tests / Vbump (#19717)

* add further testing

* vbump version

* add successfully test

* address comments
This commit is contained in:
Vasu Bhog
2022-06-14 14:47:36 -07:00
committed by GitHub
parent ba82444229
commit 6ab09d9b1b
8 changed files with 384 additions and 70 deletions

View File

@@ -2,14 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import * as path from 'path';
import * as should from 'should';
import * as sinon from 'sinon';
import * as constants from '../../common/constants';
import * as vscode from 'vscode';
import * as azureFunctionsUtils from '../../common/azureFunctionsUtils';
import * as constants from '../../common/constants';
import * as utils from '../../common/utils';
import * as azureFunctionsContracts from '../../contracts/azureFunctions/azureFunctionsContracts';
import { BindingType } from 'sql-bindings';
import { IConnectionInfo } from 'vscode-mssql';
import { createTestCredentials, createTestUtils, TestUtils } from '../testUtils';
@@ -24,7 +27,7 @@ describe('AzureFunctionUtils', function (): void {
describe('Local.Settings.Json', function (): void {
it('Should correctly parse local.settings.json', async () => {
sinon.stub(fs, 'existsSync').withArgs(localSettingsPath).returns(true);
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
`{"IsEncrypted": false,
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
@@ -35,7 +38,7 @@ describe('AzureFunctionUtils', function (): void {
});
it('setLocalAppSetting can update settings.json with new setting value', async () => {
sinon.stub(fs, 'existsSync').withArgs(localSettingsPath).returns(true);
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
`{"IsEncrypted": false,
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
@@ -47,7 +50,7 @@ describe('AzureFunctionUtils', function (): void {
});
it('Should not overwrite setting if value already exists in local.settings.json', async () => {
sinon.stub(fs, 'existsSync').withArgs(localSettingsPath).returns(true);
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
`{"IsEncrypted": false,
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
@@ -67,7 +70,7 @@ describe('AzureFunctionUtils', function (): void {
});
it('Should add connection string to local.settings.json', async () => {
sinon.stub(fs, 'existsSync').withArgs(localSettingsPath).returns(true);
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
`{"IsEncrypted": false,
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
@@ -236,6 +239,123 @@ describe('AzureFunctionUtils', function (): void {
});
});
describe('Get Azure Function Project', function (): void {
it('Should return undefined if no azure function projects are found', async () => {
// set workspace folder for testing
sinon.replaceGetter(vscode.workspace, 'workspaceFolders', () => {
return <vscode.WorkspaceFolder[]>[{
uri: {
fsPath: '/temp/'
},
}];
});
let findFilesStub = sinon.stub(vscode.workspace, 'findFiles');
findFilesStub.onFirstCall().resolves([]);
findFilesStub.onSecondCall().resolves(undefined);
let result = await azureFunctionsUtils.getAzureFunctionProject();
should(result).be.equal(undefined, 'Should be undefined since no azure function projects are found');
});
it('Should return selectedProjectFile if only one azure function project is found', async () => {
// set workspace folder for testing
sinon.replaceGetter(vscode.workspace, 'workspaceFolders', () => {
return <vscode.WorkspaceFolder[]>[{
uri: {
fsPath: '/temp/'
},
}];
});
// only one azure function project found - hostFiles and csproj files stubs
let findFilesStub = sinon.stub(vscode.workspace, 'findFiles');
findFilesStub.onFirstCall().resolves([vscode.Uri.file('/temp/host.json')]);
findFilesStub.onSecondCall().returns(Promise.resolve([vscode.Uri.file('/temp/test.csproj')]) as any);
let result = await azureFunctionsUtils.getAzureFunctionProject();
should(result).be.equal('/temp/test.csproj', 'Should return test.csproj since only one Azure function project is found');
});
it('Should return prompt to choose azure function project if multiple azure function projects are found', async () => {
// set workspace folder for testing
sinon.replaceGetter(vscode.workspace, 'workspaceFolders', () => {
return <vscode.WorkspaceFolder[]>[{
uri: {
fsPath: '/temp/'
},
}];
});
// multiple azure function projects found in workspace - hostFiles and project find files stubs
let findFilesStub = sinon.stub(vscode.workspace, 'findFiles');
findFilesStub.onFirstCall().returns(Promise.resolve([vscode.Uri.file('/temp/host.json'), vscode.Uri.file('/temp2/host.json')]) as any);
// we loop through the hostFiles to find the csproj in same directory
// first loop we use host of /temp/host.json
findFilesStub.onSecondCall().returns(Promise.resolve([vscode.Uri.file('/temp/test.csproj')]) as any);
// second loop we use host of /temp2/host.json
findFilesStub.onThirdCall().returns(Promise.resolve([vscode.Uri.file('/temp2/test.csproj')]) as any);
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').returns(Promise.resolve('/temp/test.csproj') as any);
let result = await azureFunctionsUtils.getAzureFunctionProject();
should(result).be.equal('/temp/test.csproj', 'Should return test.csproj since user choose Azure function project');
should(quickPickStub.calledOnce).be.true('showQuickPick should have been called to choose between azure function projects');
});
});
describe('PromptForObjectName', function (): void {
it('Should prompt user to enter object name manually when no connection info given', async () => {
let promptStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('test');
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input);
should(promptStub.calledOnce).be.true('showInputBox should have been called');
should(result).be.equal('test', 'Should return test since user manually entered object name');
});
it('Should return undefined when mssql connection error', async () => {
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
let connectionInfo: IConnectionInfo = createTestCredentials();// Mocks promptForConnection
let promptStub = sinon.stub(vscode.window, 'showInputBox');
sinon.stub(azureFunctionsUtils, 'getConnectionURI').resolves(undefined);
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input, connectionInfo);
should(promptStub.notCalled).be.true('showInputBox should not have been called');
should(result).be.equal(undefined, 'Should return undefined due to mssql connection error');
});
it('Should return undefined if no database selected', async () => {
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
let connectionInfo: IConnectionInfo = createTestCredentials();// Mocks promptForConnection
let promptStub = sinon.stub(vscode.window, 'showInputBox');
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
sinon.stub(vscode.window, 'showQuickPick').resolves(undefined);
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input, connectionInfo);
should(promptStub.notCalled).be.true('showInputBox should not have been called');
should(result).be.equal(undefined, 'Should return undefined due to no database selected');
});
it('Should successfully select object name', async () => {
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
let connectionInfo: IConnectionInfo = createTestCredentials();// Mocks promptForConnection
let promptStub = sinon.stub(vscode.window, 'showInputBox');
// getConnectionURI stub
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
// promptSelectDatabase stub
testUtils.vscodeMssqlIExtension.setup(x => x.listDatabases('testConnectionURI')).returns(() => Promise.resolve(['testDb']));
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves('testDb' as any);
// get tables from selected database
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionsUtils.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.onSecondCall().returns(Promise.resolve('[schema].[testTable]') as any);
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input, connectionInfo);
should(promptStub.notCalled).be.true('showInputBox should not have been called');
should(quickPickStub.calledTwice).be.true('showQuickPick should have been called twice');
should(connectionInfo.database).be.equal('testDb', 'Should have connectionInfo.database to testDb after user selects database');
should(result).be.equal('[schema].[testTable]', 'Should return [schema].[testTable] since user selected table');
});
});
afterEach(function (): void {
sinon.restore();
});

View File

@@ -0,0 +1,56 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as should from 'should';
import * as sinon from 'sinon';
import * as constants from '../../common/constants';
import { getErrorType, getUniqueFileName, TimeoutError, validateFunctionName } from '../../common/utils';
describe('Utils', function (): void {
it('Should return undefined when no folderPath given to create unique file name', async () => {
let testFile = 'testFile';
let result = await getUniqueFileName(testFile);
should(result).be.equal(undefined, 'Should return undefined since no folderPath given');
});
it('Should create unique file name if one exists', async () => {
let testFile = 'testFile';
let testFolder = 'testFolder';
let fileAccessStub = sinon.stub(fs.promises, 'access').onFirstCall().resolves();
fileAccessStub.onSecondCall().throws();
let result = await getUniqueFileName(testFile, testFolder);
should(result).be.equal('testFile1', 'Should return testFile1 since one testFile exists');
});
it('Should create unique file name if multiple versions of the file exists', async () => {
let testFile = 'testFile';
let testFolder = 'testFolder';
let fileAccessStub = sinon.stub(fs.promises, 'access').onFirstCall().resolves();
fileAccessStub.onSecondCall().resolves();
fileAccessStub.onThirdCall().throws();
let result = await getUniqueFileName(testFile, testFolder);
should(result).be.equal('testFile2', 'Should return testFile2 since both testFile1 and testFile exists');
});
it('Should validate function name', async () => {
should(validateFunctionName('')).be.equal(constants.nameMustNotBeEmpty);
should(validateFunctionName('@$%@%@%')).be.equal(constants.hasSpecialCharacters);
should(validateFunctionName('test')).be.equal(undefined);
});
it('Should get error type', async () => {
should(getErrorType(new Error('test'))).be.equal('UnknownError');
should(getErrorType(new TimeoutError('test'))).be.equal('TimeoutError');
});
afterEach(function (): void {
sinon.restore();
});
});