mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
[SQL Bindings] Skip prompt to select setting from connection settings if there is none (#19798)
* add no setting option * add tests * reformat tests
This commit is contained in:
@@ -362,87 +362,43 @@ export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.
|
|||||||
|
|
||||||
// show the settings from project's local.settings.json if there's an AF functions project
|
// show the settings from project's local.settings.json if there's an AF functions project
|
||||||
if (projectUri) {
|
if (projectUri) {
|
||||||
let settings;
|
// get existing connection string settings from project's local.settings.json file
|
||||||
try {
|
// if an error occurs getLocalSettingsJson will throw an error
|
||||||
settings = await getLocalSettingsJson(path.join(path.dirname(projectUri.fsPath!), constants.azureFunctionLocalSettingsFileName));
|
let existingSettings = await getLocalSettingsJson(path.join(path.dirname(projectUri.fsPath!), constants.azureFunctionLocalSettingsFileName));
|
||||||
} catch (e) {
|
|
||||||
void vscode.window.showErrorMessage(utils.getErrorMessage(e));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Known Azure settings reference for Azure Functions
|
// setup connection string setting quickpick
|
||||||
// https://docs.microsoft.com/en-us/azure/azure-functions/functions-app-settings
|
|
||||||
const knownSettings: string[] = [
|
|
||||||
'APPINSIGHTS_INSTRUMENTATIONKEY',
|
|
||||||
'APPLICATIONINSIGHTS_CONNECTION_STRING',
|
|
||||||
'AZURE_FUNCTION_PROXY_DISABLE_LOCAL_CALL',
|
|
||||||
'AZURE_FUNCTION_PROXY_BACKEND_URL_DECODE_SLASHES',
|
|
||||||
'AZURE_FUNCTIONS_ENVIRONMENT',
|
|
||||||
'AzureWebJobsDashboard',
|
|
||||||
'AzureWebJobsDisableHomepage',
|
|
||||||
'AzureWebJobsDotNetReleaseCompilation',
|
|
||||||
'AzureWebJobsFeatureFlags',
|
|
||||||
'AzureWebJobsKubernetesSecretName',
|
|
||||||
'AzureWebJobsSecretStorageKeyVaultClientId',
|
|
||||||
'AzureWebJobsSecretStorageKeyVaultClientSecret',
|
|
||||||
'AzureWebJobsSecretStorageKeyVaultName',
|
|
||||||
'AzureWebJobsSecretStorageKeyVaultTenantId',
|
|
||||||
'AzureWebJobsSecretStorageKeyVaultUri',
|
|
||||||
'AzureWebJobsSecretStorageSas',
|
|
||||||
'AzureWebJobsSecretStorageType',
|
|
||||||
'AzureWebJobsStorage',
|
|
||||||
'AzureWebJobs_TypeScriptPath',
|
|
||||||
'DOCKER_SHM_SIZE',
|
|
||||||
'FUNCTION_APP_EDIT_MODE',
|
|
||||||
'FUNCTIONS_EXTENSION_VERSION',
|
|
||||||
'FUNCTIONS_V2_COMPATIBILITY_MODE',
|
|
||||||
'FUNCTIONS_WORKER_PROCESS_COUNT',
|
|
||||||
'FUNCTIONS_WORKER_RUNTIME',
|
|
||||||
'FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED',
|
|
||||||
'MDMaxBackgroundUpgradePeriod',
|
|
||||||
'MDNewSnapshotCheckPeriod',
|
|
||||||
'MDMinBackgroundUpgradePeriod',
|
|
||||||
'PIP_EXTRA_INDEX_URL',
|
|
||||||
'PYTHON_ISOLATE_WORKER_DEPENDENCIES (Preview)',
|
|
||||||
'PYTHON_ENABLE_DEBUG_LOGGING',
|
|
||||||
'PYTHON_ENABLE_WORKER_EXTENSIONS',
|
|
||||||
'PYTHON_THREADPOOL_THREAD_COUNT',
|
|
||||||
'SCALE_CONTROLLER_LOGGING_ENABLED',
|
|
||||||
'SCM_LOGSTREAM_TIMEOUT',
|
|
||||||
'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING',
|
|
||||||
'WEBSITE_CONTENTOVERVNET',
|
|
||||||
'WEBSITE_CONTENTSHARE',
|
|
||||||
'WEBSITE_SKIP_CONTENTSHARE_VALIDATION',
|
|
||||||
'WEBSITE_DNS_SERVER',
|
|
||||||
'WEBSITE_ENABLE_BROTLI_ENCODING',
|
|
||||||
'WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT',
|
|
||||||
'WEBSITE_NODE_DEFAULT_VERSION',
|
|
||||||
'WEBSITE_RUN_FROM_PACKAGE',
|
|
||||||
'WEBSITE_TIME_ZONE',
|
|
||||||
'WEBSITE_VNET_ROUTE_ALL'
|
|
||||||
];
|
|
||||||
|
|
||||||
// setup connetion string setting quickpick
|
|
||||||
let connectionStringSettings: (vscode.QuickPickItem)[] = [];
|
let connectionStringSettings: (vscode.QuickPickItem)[] = [];
|
||||||
if (settings?.Values) {
|
let hasNonFilteredSettings: boolean = false;
|
||||||
connectionStringSettings = Object.keys(settings.Values).filter(setting => !knownSettings.includes(setting)).map(setting => { return { label: setting }; });
|
if (existingSettings?.Values && Object.keys(existingSettings?.Values!).length > 0) {
|
||||||
|
// add settings found in local.settings.json to quickpick list
|
||||||
|
connectionStringSettings = Object.keys(existingSettings.Values).filter(setting => !constants.knownSettings.includes(setting)).map(setting => { return { label: setting }; });
|
||||||
|
// set boolean to true if there are non-filtered settings
|
||||||
|
hasNonFilteredSettings = connectionStringSettings.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add create new setting option to quickpick list
|
||||||
connectionStringSettings.unshift({ label: constants.createNewLocalAppSettingWithIcon });
|
connectionStringSettings.unshift({ label: constants.createNewLocalAppSettingWithIcon });
|
||||||
let sqlConnectionStringSettingExists = connectionStringSettings.find(s => s.label === constants.sqlConnectionStringSetting);
|
|
||||||
|
|
||||||
while (!connectionStringSettingName) {
|
while (!connectionStringSettingName) {
|
||||||
const selectedSetting = await vscode.window.showQuickPick(connectionStringSettings, {
|
let selectedSetting: vscode.QuickPickItem | undefined;
|
||||||
canPickMany: false,
|
// prompt user to select a setting from the list or create a new one
|
||||||
title: constants.selectSetting,
|
// only if there are existing setting values are found and has non-filtered settings
|
||||||
ignoreFocusOut: true
|
if (hasNonFilteredSettings) {
|
||||||
});
|
selectedSetting = await vscode.window.showQuickPick(connectionStringSettings, {
|
||||||
if (!selectedSetting) {
|
canPickMany: false,
|
||||||
// User cancelled
|
title: constants.selectSetting,
|
||||||
return;
|
ignoreFocusOut: true
|
||||||
|
});
|
||||||
|
if (!selectedSetting) {
|
||||||
|
// User cancelled
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedSetting.label === constants.createNewLocalAppSettingWithIcon) {
|
// prompt user to enter connection string setting name if user selects create new setting or there is no existing settings in local.settings.json
|
||||||
|
if (selectedSetting?.label === constants.createNewLocalAppSettingWithIcon || !hasNonFilteredSettings) {
|
||||||
|
let sqlConnectionStringSettingExists = connectionStringSettings.find(s => s.label === constants.sqlConnectionStringSetting);
|
||||||
|
// prompt user to enter connection string setting name manually
|
||||||
const newConnectionStringSettingName = await vscode.window.showInputBox(
|
const newConnectionStringSettingName = await vscode.window.showInputBox(
|
||||||
{
|
{
|
||||||
title: constants.enterConnectionStringSettingName,
|
title: constants.enterConnectionStringSettingName,
|
||||||
@@ -452,9 +408,13 @@ export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.
|
|||||||
}
|
}
|
||||||
) ?? '';
|
) ?? '';
|
||||||
|
|
||||||
if (!newConnectionStringSettingName) {
|
if (!newConnectionStringSettingName && hasNonFilteredSettings) {
|
||||||
// go back to select setting quickpick if user escapes from inputting the setting name in case they changed their mind
|
// go back to select setting quickpick if user escapes from entering in the connection string setting name
|
||||||
|
// only go back if there are existing settings in local.settings.json
|
||||||
continue;
|
continue;
|
||||||
|
} else if (!newConnectionStringSettingName && !hasNonFilteredSettings) {
|
||||||
|
// User cancelled out of the manually enter connection string prompt
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedConnectionStringMethod: string | undefined;
|
let selectedConnectionStringMethod: string | undefined;
|
||||||
@@ -477,7 +437,7 @@ export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selectedConnectionStringMethod === constants.userConnectionString) {
|
if (selectedConnectionStringMethod === constants.userConnectionString) {
|
||||||
// User chooses to enter connection string manually
|
// prompt user to enter connection string manually
|
||||||
connectionString = await vscode.window.showInputBox(
|
connectionString = await vscode.window.showInputBox(
|
||||||
{
|
{
|
||||||
title: constants.enterConnectionString,
|
title: constants.enterConnectionString,
|
||||||
@@ -515,19 +475,19 @@ export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.
|
|||||||
connectionStringSettingName = newConnectionStringSettingName;
|
connectionStringSettingName = newConnectionStringSettingName;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
void vscode.window.showErrorMessage(constants.selectConnectionError());
|
void vscode.window.showErrorMessage(constants.failedToSetSetting());
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// display error message and show select setting quickpick again
|
// display error message and show select setting quickpick again
|
||||||
void vscode.window.showErrorMessage(constants.selectConnectionError(e));
|
void vscode.window.showErrorMessage(constants.failedToSetSetting(e));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If user cancels out of this or doesn't want to overwrite an existing setting
|
// If user cancels out of this or doesn't want to overwrite an existing setting
|
||||||
// just return them to the select setting quickpick in case they changed their mind
|
// just return them to the select setting quickpick in case they changed their mind
|
||||||
connectionStringSettingName = selectedSetting.label;
|
connectionStringSettingName = selectedSetting?.label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add sql extension package reference to project. If the reference is already there, it doesn't get added again
|
// Add sql extension package reference to project. If the reference is already there, it doesn't get added again
|
||||||
|
|||||||
@@ -78,10 +78,62 @@ export const enterObjectName = localize('enterObjectName', 'Enter SQL table or v
|
|||||||
export const enterObjectNameToUpsert = localize('enterObjectNameToUpsert', 'Enter SQL table to upsert into');
|
export const enterObjectNameToUpsert = localize('enterObjectNameToUpsert', 'Enter SQL table to upsert into');
|
||||||
export const selectTable = localize('selectTable', 'Select table to use');
|
export const selectTable = localize('selectTable', 'Select table to use');
|
||||||
export const tableListProgressTitle = localize('tableListProgressTitle', "Fetching tables for selected database...");
|
export const tableListProgressTitle = localize('tableListProgressTitle', "Fetching tables for selected database...");
|
||||||
export const selectConnectionError = (err?: any): string => err ? localize('selectConnectionError', "Failed to set connection string app setting: {0}", utils.getErrorMessage(err)) : localize('unableToSetConnectionString', "Failed to set connection string app setting");
|
export const failedToSetSetting = (err?: any): string => err ? localize('failedToSetSetting', "Failed to set connection string app setting: {0}", utils.getErrorMessage(err)) : localize('unableToSetConnectionString', "Failed to set connection string app setting");
|
||||||
export function selectBindingType(funcName?: string): string { return funcName ? localize('selectBindingTypeToSpecifiedFunction', "Select type of binding for the function '{0}'", funcName) : localize('selectBindingType', "Select type of binding"); }
|
export function selectBindingType(funcName?: string): string { return funcName ? localize('selectBindingTypeToSpecifiedFunction', "Select type of binding for the function '{0}'", funcName) : localize('selectBindingType', "Select type of binding"); }
|
||||||
export function settingAlreadyExists(settingName: string): string { return localize('SettingAlreadyExists', 'Local app setting \'{0}\' already exists. Overwrite?', settingName); }
|
export function settingAlreadyExists(settingName: string): string { return localize('SettingAlreadyExists', 'Local app setting \'{0}\' already exists. Overwrite?', settingName); }
|
||||||
export function failedToParse(filename: string, error: any): string { return localize('failedToParse', 'Failed to parse "{0}": {1}.', filename, utils.getErrorMessage(error)); }
|
export function failedToParse(filename: string, error: any): string { return localize('failedToParse', 'Failed to parse "{0}": {1}.', filename, utils.getErrorMessage(error)); }
|
||||||
export function addSqlBinding(functionName: string): string { return localize('addSqlBinding', 'Adding SQL Binding to function "{0}"...'), functionName; }
|
export function addSqlBinding(functionName: string): string { return localize('addSqlBinding', 'Adding SQL Binding to function "{0}"...'), functionName; }
|
||||||
export function errorNewAzureFunction(error: any): string { return localize('errorNewAzureFunction', 'Error creating new Azure Function: {0}', utils.getErrorMessage(error)); }
|
export function errorNewAzureFunction(error: any): string { return localize('errorNewAzureFunction', 'Error creating new Azure Function: {0}', utils.getErrorMessage(error)); }
|
||||||
export function manuallyEnterObjectName(userObjectName: string): string { return `$(pencil) ${userObjectName}`; }
|
export function manuallyEnterObjectName(userObjectName: string): string { return `$(pencil) ${userObjectName}`; }
|
||||||
|
|
||||||
|
// Known Azure settings reference for Azure Functions
|
||||||
|
// https://docs.microsoft.com/en-us/azure/azure-functions/functions-app-settings
|
||||||
|
export const knownSettings: string[] = [
|
||||||
|
'APPINSIGHTS_INSTRUMENTATIONKEY',
|
||||||
|
'APPLICATIONINSIGHTS_CONNECTION_STRING',
|
||||||
|
'AZURE_FUNCTION_PROXY_DISABLE_LOCAL_CALL',
|
||||||
|
'AZURE_FUNCTION_PROXY_BACKEND_URL_DECODE_SLASHES',
|
||||||
|
'AZURE_FUNCTIONS_ENVIRONMENT',
|
||||||
|
'AzureWebJobsDashboard',
|
||||||
|
'AzureWebJobsDisableHomepage',
|
||||||
|
'AzureWebJobsDotNetReleaseCompilation',
|
||||||
|
'AzureWebJobsFeatureFlags',
|
||||||
|
'AzureWebJobsKubernetesSecretName',
|
||||||
|
'AzureWebJobsSecretStorageKeyVaultClientId',
|
||||||
|
'AzureWebJobsSecretStorageKeyVaultClientSecret',
|
||||||
|
'AzureWebJobsSecretStorageKeyVaultName',
|
||||||
|
'AzureWebJobsSecretStorageKeyVaultTenantId',
|
||||||
|
'AzureWebJobsSecretStorageKeyVaultUri',
|
||||||
|
'AzureWebJobsSecretStorageSas',
|
||||||
|
'AzureWebJobsSecretStorageType',
|
||||||
|
'AzureWebJobsStorage',
|
||||||
|
'AzureWebJobs_TypeScriptPath',
|
||||||
|
'DOCKER_SHM_SIZE',
|
||||||
|
'FUNCTION_APP_EDIT_MODE',
|
||||||
|
'FUNCTIONS_EXTENSION_VERSION',
|
||||||
|
'FUNCTIONS_V2_COMPATIBILITY_MODE',
|
||||||
|
'FUNCTIONS_WORKER_PROCESS_COUNT',
|
||||||
|
'FUNCTIONS_WORKER_RUNTIME',
|
||||||
|
'FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED',
|
||||||
|
'MDMaxBackgroundUpgradePeriod',
|
||||||
|
'MDNewSnapshotCheckPeriod',
|
||||||
|
'MDMinBackgroundUpgradePeriod',
|
||||||
|
'PIP_EXTRA_INDEX_URL',
|
||||||
|
'PYTHON_ISOLATE_WORKER_DEPENDENCIES (Preview)',
|
||||||
|
'PYTHON_ENABLE_DEBUG_LOGGING',
|
||||||
|
'PYTHON_ENABLE_WORKER_EXTENSIONS',
|
||||||
|
'PYTHON_THREADPOOL_THREAD_COUNT',
|
||||||
|
'SCALE_CONTROLLER_LOGGING_ENABLED',
|
||||||
|
'SCM_LOGSTREAM_TIMEOUT',
|
||||||
|
'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING',
|
||||||
|
'WEBSITE_CONTENTOVERVNET',
|
||||||
|
'WEBSITE_CONTENTSHARE',
|
||||||
|
'WEBSITE_SKIP_CONTENTSHARE_VALIDATION',
|
||||||
|
'WEBSITE_DNS_SERVER',
|
||||||
|
'WEBSITE_ENABLE_BROTLI_ENCODING',
|
||||||
|
'WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT',
|
||||||
|
'WEBSITE_NODE_DEFAULT_VERSION',
|
||||||
|
'WEBSITE_RUN_FROM_PACKAGE',
|
||||||
|
'WEBSITE_TIME_ZONE',
|
||||||
|
'WEBSITE_VNET_ROUTE_ALL'
|
||||||
|
];
|
||||||
|
|||||||
@@ -26,36 +26,27 @@ describe('AzureFunctionUtils', function (): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Local.Settings.Json', function (): void {
|
describe('Local.Settings.Json', function (): void {
|
||||||
it('Should correctly parse local.settings.json', async () => {
|
beforeEach(function (): void {
|
||||||
|
// create fake connection string settings for local.setting.json to be used
|
||||||
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
||||||
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
|
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
|
||||||
`{"IsEncrypted": false,
|
`{"IsEncrypted": false,
|
||||||
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
it('Should correctly parse local.settings.json', async () => {
|
||||||
let settings = await azureFunctionsUtils.getLocalSettingsJson(localSettingsPath);
|
let settings = await azureFunctionsUtils.getLocalSettingsJson(localSettingsPath);
|
||||||
should(settings.IsEncrypted).equals(false);
|
should(settings.IsEncrypted).equals(false);
|
||||||
should(Object.keys(settings.Values!).length).equals(3);
|
should(Object.keys(settings.Values!).length).equals(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('setLocalAppSetting can update settings.json with new setting value', async () => {
|
it('setLocalAppSetting can update settings.json with new setting value', async () => {
|
||||||
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
|
||||||
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
|
|
||||||
`{"IsEncrypted": false,
|
|
||||||
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
|
||||||
);
|
|
||||||
|
|
||||||
let writeFileStub = sinon.stub(fs.promises, 'writeFile').resolves();
|
let writeFileStub = sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
await azureFunctionsUtils.setLocalAppSetting(path.dirname(localSettingsPath), 'test4', 'test4');
|
await azureFunctionsUtils.setLocalAppSetting(path.dirname(localSettingsPath), 'test4', 'test4');
|
||||||
should(writeFileStub.calledWithExactly(localSettingsPath, `{\n "IsEncrypted": false,\n "Values": {\n "test1": "test1",\n "test2": "test2",\n "test3": "test3",\n "test4": "test4"\n }\n}`)).equals(true, 'writeFile should be called with the correct arguments');
|
should(writeFileStub.calledWithExactly(localSettingsPath, `{\n "IsEncrypted": false,\n "Values": {\n "test1": "test1",\n "test2": "test2",\n "test3": "test3",\n "test4": "test4"\n }\n}`)).equals(true, 'writeFile should be called with the correct arguments');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should not overwrite setting if value already exists in local.settings.json', async () => {
|
it('Should not overwrite setting if value already exists in local.settings.json', async () => {
|
||||||
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
|
||||||
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
|
|
||||||
`{"IsEncrypted": false,
|
|
||||||
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
|
||||||
);
|
|
||||||
|
|
||||||
let warningMsg = constants.settingAlreadyExists('test1');
|
let warningMsg = constants.settingAlreadyExists('test1');
|
||||||
const showErrorMessageSpy = sinon.stub(vscode.window, 'showWarningMessage').resolves({ title: constants.settingAlreadyExists('test1') });
|
const showErrorMessageSpy = sinon.stub(vscode.window, 'showWarningMessage').resolves({ title: constants.settingAlreadyExists('test1') });
|
||||||
|
|
||||||
@@ -70,11 +61,6 @@ describe('AzureFunctionUtils', function (): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should add connection string to local.settings.json', async () => {
|
it('Should add connection string to local.settings.json', async () => {
|
||||||
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
|
||||||
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
|
|
||||||
`{"IsEncrypted": false,
|
|
||||||
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
|
||||||
);
|
|
||||||
const connectionString = 'testConnectionString';
|
const connectionString = 'testConnectionString';
|
||||||
|
|
||||||
let writeFileStub = sinon.stub(fs.promises, 'writeFile').resolves();
|
let writeFileStub = sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
@@ -84,6 +70,14 @@ describe('AzureFunctionUtils', function (): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Password Prompts', function (): void {
|
describe('Password Prompts', function (): void {
|
||||||
|
beforeEach(function (): void {
|
||||||
|
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
||||||
|
sinon.stub(fs, 'readFileSync').withArgs(localSettingsPath).returns(
|
||||||
|
`{"IsEncrypted": false,
|
||||||
|
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
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
|
||||||
@@ -106,7 +100,7 @@ describe('AzureFunctionUtils', function (): void {
|
|||||||
should(quickInputSpy.notCalled).be.true('showInputBox should not have been called');
|
should(quickInputSpy.notCalled).be.true('showInputBox should not have been called');
|
||||||
should(warningSpy.notCalled).be.true('showWarningMessage should not have been called');
|
should(warningSpy.notCalled).be.true('showWarningMessage should not have been called');
|
||||||
// get connection string result
|
// get connection string result
|
||||||
should(getConnectionString).equals(`Server=${connectionInfo.server};Initial Catalog=${connectionInfo.database};User ID=${connectionInfo.user};Password=${connectionInfo.password};`,'Should return a connection string with the password');
|
should(getConnectionString).equals(`Server=${connectionInfo.server};Initial Catalog=${connectionInfo.database};User ID=${connectionInfo.user};Password=${connectionInfo.password};`, 'Should return a connection string with the password');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should not include password and show warning if user does not want to include password prompt and connection info contains the password and auth type is SQL', async () => {
|
it('Should not include password and show warning if user does not want to include password prompt and connection info contains the password and auth type is SQL', async () => {
|
||||||
@@ -131,7 +125,7 @@ describe('AzureFunctionUtils', function (): void {
|
|||||||
should(quickInputSpy.notCalled).be.true('showInputBox should not have been called');
|
should(quickInputSpy.notCalled).be.true('showInputBox should not have been called');
|
||||||
should(warningSpy.calledOnce).be.true('showWarningMessage should have been called');
|
should(warningSpy.calledOnce).be.true('showWarningMessage should have been called');
|
||||||
// returned connection string should NOT include password
|
// returned connection string should NOT include password
|
||||||
should(getConnectionString).equals(`Server=${connectionInfo.server};Initial Catalog=${connectionInfo.database};User ID=${connectionInfo.user};Password=${constants.passwordPlaceholder};`,'Should return a connection string without the password');
|
should(getConnectionString).equals(`Server=${connectionInfo.server};Initial Catalog=${connectionInfo.database};User ID=${connectionInfo.user};Password=${constants.passwordPlaceholder};`, 'Should return a connection string without the password');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should not include password and show warning if user cancels include password prompt and connection info contains the password and auth type is SQL', async () => {
|
it('Should not include password and show warning if user cancels include password prompt and connection info contains the password and auth type is SQL', async () => {
|
||||||
@@ -301,40 +295,40 @@ describe('AzureFunctionUtils', function (): void {
|
|||||||
|
|
||||||
describe('PromptForObjectName', function (): void {
|
describe('PromptForObjectName', function (): void {
|
||||||
it('Should prompt user to enter object name manually when no connection info given', async () => {
|
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 inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('test');
|
||||||
|
|
||||||
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input);
|
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input);
|
||||||
should(promptStub.calledOnce).be.true('showInputBox should have been called');
|
should(inputBoxStub.calledOnce).be.true('showInputBox should have been called');
|
||||||
should(result).be.equal('test', 'Should return test since user manually entered object name');
|
should(result).be.equal('test', 'Should return test since user manually entered object name');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should return undefined when mssql connection error', async () => {
|
it('Should return undefined when mssql connection error', 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
|
||||||
let promptStub = sinon.stub(vscode.window, 'showInputBox');
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox');
|
||||||
sinon.stub(azureFunctionsUtils, 'getConnectionURI').resolves(undefined);
|
sinon.stub(azureFunctionsUtils, 'getConnectionURI').resolves(undefined);
|
||||||
|
|
||||||
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input, connectionInfo);
|
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input, connectionInfo);
|
||||||
should(promptStub.notCalled).be.true('showInputBox should not have been called');
|
should(inputBoxStub.notCalled).be.true('showInputBox should not have been called');
|
||||||
should(result).be.equal(undefined, 'Should return undefined due to mssql connection error');
|
should(result).be.equal(undefined, 'Should return undefined due to mssql connection error');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should return undefined if no database selected', async () => {
|
it('Should return undefined if no database selected', 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
|
||||||
let promptStub = sinon.stub(vscode.window, 'showInputBox');
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox');
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
||||||
sinon.stub(vscode.window, 'showQuickPick').resolves(undefined);
|
sinon.stub(vscode.window, 'showQuickPick').resolves(undefined);
|
||||||
|
|
||||||
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input, connectionInfo);
|
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input, connectionInfo);
|
||||||
should(promptStub.notCalled).be.true('showInputBox should not have been called');
|
should(inputBoxStub.notCalled).be.true('showInputBox should not have been called');
|
||||||
should(result).be.equal(undefined, 'Should return undefined due to no database selected');
|
should(result).be.equal(undefined, 'Should return undefined due to no database selected');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should successfully select object name', async () => {
|
it('Should successfully select object name', 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
|
||||||
let promptStub = sinon.stub(vscode.window, 'showInputBox');
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox');
|
||||||
// getConnectionURI stub
|
// getConnectionURI stub
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
||||||
// promptSelectDatabase stub
|
// promptSelectDatabase stub
|
||||||
@@ -349,13 +343,328 @@ describe('AzureFunctionUtils', function (): void {
|
|||||||
|
|
||||||
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input, connectionInfo);
|
let result = await azureFunctionsUtils.promptForObjectName(BindingType.input, connectionInfo);
|
||||||
|
|
||||||
should(promptStub.notCalled).be.true('showInputBox should not have been called');
|
should(inputBoxStub.notCalled).be.true('showInputBox should not have been called');
|
||||||
should(quickPickStub.calledTwice).be.true('showQuickPick should have been called twice');
|
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(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');
|
should(result).be.equal('[schema].[testTable]', 'Should return [schema].[testTable] since user selected table');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('PromptAndUpdateConnectionStringSetting', function (): void {
|
||||||
|
const fileUri = vscode.Uri.file(path.join(rootFolderPath, 'testProjectU'));
|
||||||
|
it('Should prompt user to enter connection string setting only if no azure function project uri given', async () => {
|
||||||
|
let quickPickStub = sinon.spy(vscode.window, 'showQuickPick');
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('test');
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(undefined);
|
||||||
|
should(quickPickStub.notCalled).be.true('quickPickStub should not have been called');
|
||||||
|
should(inputBoxStub.calledOnce).be.true('showInputBox should have been called');
|
||||||
|
should(result?.connectionStringSettingName).be.equal('test', 'Should return test since user manually entered connection string');
|
||||||
|
should(inputBoxStub.firstCall.args).containEql(
|
||||||
|
{
|
||||||
|
prompt: constants.connectionStringSetting,
|
||||||
|
placeHolder: constants.connectionStringSettingPlaceholder,
|
||||||
|
ignoreFocusOut: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('No local.settings.json file', function (): void {
|
||||||
|
let fileAccessStub: sinon.SinonStub;
|
||||||
|
|
||||||
|
beforeEach(function (): void {
|
||||||
|
// stubs for getLocalSettingsJson calls
|
||||||
|
// returns {IsEncrypted: False}
|
||||||
|
fileAccessStub = sinon.stub(fs.promises, 'access').onFirstCall().rejects();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should prompt user to enter connection string setting name no connection info given', async () => {
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('testConnectionStringName'); // enter connection string setting name
|
||||||
|
|
||||||
|
// no connection info given so prompt for connection info stubs
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves((constants.connectionProfile) as any);
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.promptForConnection(true)).returns(() => Promise.resolve(connectionInfo));
|
||||||
|
// passsword prompt stub
|
||||||
|
quickPickStub.onSecondCall().resolves((constants.yesString) as any);
|
||||||
|
// getConnectionString stubs - in password prompt logic
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials(); // create test connectionInfo
|
||||||
|
let connectionDetails = { options: connectionInfo };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
// setLocalAppSetting stubs
|
||||||
|
fileAccessStub.onSecondCall().rejects(); // getLocalSettingsJson stub
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
|
sinon.stub(azureFunctionsUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'testConnectionStringName', 'testConnectionString').resolves((true));
|
||||||
|
// addSqlNugetReferenceToProjectFile stub
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri);
|
||||||
|
|
||||||
|
should(inputBoxStub.calledOnce).be.true('showInputBox should have been called');
|
||||||
|
should(quickPickStub.callCount).be.equal(2, 'quickPickStub should have been called');
|
||||||
|
should(result?.connectionStringSettingName).be.equal('testConnectionStringName', 'Should return testConnectionStringName from manually entered connection string name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should prompt user to enter connection string setting name when connection info given', async () => {
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('testConnectionStringName'); // enter connection string setting name
|
||||||
|
|
||||||
|
// password prompt stub
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves((constants.yesString) as any);
|
||||||
|
// getConnectionString stubs - in password prompt logic
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials(); // create test connectionInfo
|
||||||
|
let connectionDetails = { options: connectionInfo };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
// setLocalAppSetting stubs
|
||||||
|
fileAccessStub.onSecondCall().rejects(); // getLocalSettingsJson stub
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
|
sinon.stub(azureFunctionsUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'testConnectionStringName', 'testConnectionString').resolves((true));
|
||||||
|
// addSqlNugetReferenceToProjectFile stub
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri, connectionInfo);
|
||||||
|
|
||||||
|
should(inputBoxStub.calledOnce).be.true('showInputBox should have been called');
|
||||||
|
should(quickPickStub.callCount).be.equal(1, 'quickPickStub should have been called');
|
||||||
|
should(result?.connectionStringSettingName).be.equal('testConnectionStringName', 'Should return testConnectionStringName from manually entered connection string name');
|
||||||
|
should(result?.connectionInfo).be.equal(connectionInfo, 'Should return connectionInfo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return when user cancels out of manually entering connection string name prompt and has no existing connection string in local.settings.json', async () => {
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves(undefined); // user cancels out of connection string setting name
|
||||||
|
let quickPickSpy = sinon.spy(vscode.window, 'showQuickPick');
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri);
|
||||||
|
|
||||||
|
should(inputBoxStub.calledOnce).be.true('showInputBox should have been called');
|
||||||
|
should(quickPickSpy.callCount).be.equal(0, 'quickPickStub should have been called');
|
||||||
|
should(result?.connectionStringSettingName).be.equal(undefined, 'Should return undefined since user cancelled out of connection string setting name prompt');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('local.settings.json file contains non-filtered connection setting tests', function (): void {
|
||||||
|
|
||||||
|
beforeEach(function (): void {
|
||||||
|
// create fake connection string settings for local.setting.json to be used
|
||||||
|
// getLocalSettingsJson stub
|
||||||
|
sinon.stub(fs.promises, 'access').resolves();
|
||||||
|
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
||||||
|
`{"IsEncrypted": false,
|
||||||
|
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should use user entered connection string setting name when non-filtered connection strings local.settings.json', async () => {
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.createNewLocalAppSettingWithIcon }); // user chooses to create new connection string setting name
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('testConnectionStringName'); // enter connection string setting name
|
||||||
|
|
||||||
|
// password prompt stub
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
quickPickStub.onSecondCall().resolves((constants.yesString) as any);
|
||||||
|
// getConnectionString stubs - in password prompt logic
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials(); // create test connectionInfo
|
||||||
|
let connectionDetails = { options: connectionInfo };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
|
sinon.stub(azureFunctionsUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'testConnectionStringName', 'testConnectionString').resolves((true));
|
||||||
|
// addSqlNugetReferenceToProjectFile stub
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri, connectionInfo);
|
||||||
|
|
||||||
|
should(inputBoxStub.calledOnce).be.true('showInputBox should have been called');
|
||||||
|
should(quickPickStub.callCount).be.equal(2, 'showQuickPick should have been called');
|
||||||
|
should(result?.connectionStringSettingName).be.equal('testConnectionStringName', 'Should return testConnectionStringName from manually entered connection string name');
|
||||||
|
should(result?.connectionInfo).be.equal(connectionInfo, 'Should return connectionInfo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should use existing connection string when there are non-filtered connection strings found in local.settings.json', async () => {
|
||||||
|
let inputBoxSpy = sinon.spy(vscode.window, 'showInputBox');
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: 'test1' }); // user chooses existing setting name
|
||||||
|
|
||||||
|
// addSqlNugetReferenceToProjectFile stub
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials(); // create test connectionInfo
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri, connectionInfo);
|
||||||
|
|
||||||
|
should(inputBoxSpy.notCalled).be.true('showInputBox should not have been called');
|
||||||
|
should(quickPickStub.callCount).be.equal(1, 'showQuickPick should have been called');
|
||||||
|
should(result?.connectionStringSettingName).be.equal('test1', 'Should return test1 setting chosen from quickpick');
|
||||||
|
should(result?.connectionInfo).be.equal(connectionInfo, 'Should return connectionInfo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should use user entered connection string setting name and manually enter connection string when no connection info given', async () => {
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.createNewLocalAppSettingWithIcon }); // user chooses to create new connection string setting name
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('testConnectionStringName'); // enter connection string setting name
|
||||||
|
|
||||||
|
// user chooses to manually enter connection string
|
||||||
|
quickPickStub.onSecondCall().resolves((constants.userConnectionString) as any);
|
||||||
|
inputBoxStub.onSecondCall().resolves('testConnectionString');
|
||||||
|
|
||||||
|
// setLocalAppSetting stubs
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
|
sinon.stub(azureFunctionsUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'testConnectionStringName', 'testConnectionString').resolves((true));
|
||||||
|
// addSqlNugetReferenceToProjectFile stub
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri);
|
||||||
|
|
||||||
|
should(inputBoxStub.callCount).be.equal(2, 'showInputBox should have been called');
|
||||||
|
should(quickPickStub.callCount).be.equal(2, 'showQuickPick should have been called');
|
||||||
|
should(result?.connectionStringSettingName).be.equal('testConnectionStringName', 'Should return testConnectionStringName from manually entered connection string name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should prompt connection string method when user cancels out of selecting connection profile', async () => {
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.createNewLocalAppSettingWithIcon }); // user chooses to create new connection string setting name
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('testConnectionStringName'); // enter connection string setting name
|
||||||
|
|
||||||
|
// user chooses to manually enter connection string
|
||||||
|
quickPickStub.onSecondCall().resolves((constants.userConnectionString) as any);
|
||||||
|
inputBoxStub.onSecondCall().resolves(undefined);
|
||||||
|
|
||||||
|
quickPickStub.onThirdCall().resolves(undefined);
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri);
|
||||||
|
|
||||||
|
should(quickPickStub.getCall(1).args).containDeepOrdered([
|
||||||
|
[constants.connectionProfile, constants.userConnectionString],
|
||||||
|
{
|
||||||
|
canPickMany: false,
|
||||||
|
title: constants.selectConnectionString,
|
||||||
|
ignoreFocusOut: true
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
should(inputBoxStub.callCount).be.equal(2, 'showInputBox should have been called twice');
|
||||||
|
should(quickPickStub.callCount).be.equal(3, 'showQuickPick should have been called three times');
|
||||||
|
should(result?.connectionStringSettingName).be.equal(undefined, 'Should return undefined since user cancelled out of connection string setting name prompt');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should prompt connection string method when user cancels out of manually entering connection string', async () => {
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.createNewLocalAppSettingWithIcon }); // user chooses to create new connection string setting name
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('testConnectionStringName'); // enter connection string setting name
|
||||||
|
|
||||||
|
// user chooses to manually enter connection string
|
||||||
|
quickPickStub.onSecondCall().resolves((constants.connectionProfile) as any);
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
// user cancels out of connection profile prompt
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.promptForConnection(true)).returns(() => Promise.resolve(undefined));
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri);
|
||||||
|
|
||||||
|
should(quickPickStub.getCall(2).args).containDeepOrdered([
|
||||||
|
[constants.connectionProfile, constants.userConnectionString],
|
||||||
|
{
|
||||||
|
canPickMany: false,
|
||||||
|
title: constants.selectConnectionString,
|
||||||
|
ignoreFocusOut: true
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
should(inputBoxStub.callCount).be.equal(1, 'showInputBox should have been called');
|
||||||
|
should(quickPickStub.callCount).be.equal(3, 'showQuickPick should have been called three times');
|
||||||
|
should(result?.connectionStringSettingName).be.equal(undefined, 'Should return undefined since user cancelled out of connection string setting name prompt');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should prompt connection string settings when user cancels out of manually entering connection string name prompt', async () => {
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.createNewLocalAppSettingWithIcon }); // user chooses to create new connection string setting name
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves(undefined); // enter connection string setting name
|
||||||
|
|
||||||
|
// cancel out of prompt for connection string settings
|
||||||
|
quickPickStub.onSecondCall().resolves(undefined);
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri);
|
||||||
|
|
||||||
|
should(inputBoxStub.callCount).be.equal(1, 'showInputBox should have been called');
|
||||||
|
should(quickPickStub.callCount).be.equal(2, 'showQuickPick should have been called');
|
||||||
|
should(quickPickStub.getCall(1).args).containDeepOrdered([
|
||||||
|
[{ label: constants.createNewLocalAppSettingWithIcon }, { label: 'test1' }, { label: 'test2' }, { label: 'test3' }],
|
||||||
|
{
|
||||||
|
canPickMany: false,
|
||||||
|
title: constants.selectSetting,
|
||||||
|
ignoreFocusOut: true
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
should(result?.connectionStringSettingName).be.equal(undefined, 'Should return undefined since user cancelled out of connection string setting name prompt');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should prompt user to enter connection string setting name when local.settings.json values contains known connection strings', async () => {
|
||||||
|
// create fake connection string settings for local.setting.json to be used
|
||||||
|
// getLocalSettingsJson stub
|
||||||
|
sinon.stub(fs.promises, 'access').resolves();
|
||||||
|
// known connection string values that will be filtered out
|
||||||
|
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
||||||
|
`{"IsEncrypted": false,
|
||||||
|
"Values": {"AzureWebJobsStorage": "testWebJobStorage","WEBSITE_TIME_ZONE":"testTimeZone"}}`
|
||||||
|
);
|
||||||
|
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('testConnectionStringName'); // enter connection string setting name
|
||||||
|
|
||||||
|
// password prompt stub
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves((constants.yesString) as any);
|
||||||
|
// getConnectionString stubs - in password prompt logic
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials(); // create test connectionInfo
|
||||||
|
let connectionDetails = { options: connectionInfo };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
|
sinon.stub(azureFunctionsUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'testConnectionStringName', 'testConnectionString').resolves((true));
|
||||||
|
// addSqlNugetReferenceToProjectFile stub
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri, connectionInfo);
|
||||||
|
|
||||||
|
should(inputBoxStub.calledOnce).be.true('showInputBox should have been called');
|
||||||
|
should(quickPickStub.callCount).be.equal(1, 'quickPickStub should have been called');
|
||||||
|
should(result?.connectionStringSettingName).be.equal('testConnectionStringName', 'Should return testConnectionStringName from manually entered connection string name');
|
||||||
|
should(result?.connectionInfo).be.equal(connectionInfo, 'Should return connectionInfo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should prompt user to enter connection string setting name when local.settings.json values are empty', async () => {
|
||||||
|
// create fake connection string settings for local.setting.json to be used
|
||||||
|
// getLocalSettingsJson stub
|
||||||
|
sinon.stub(fs.promises, 'access').resolves();
|
||||||
|
// empty values in local.settings.json
|
||||||
|
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
||||||
|
`{"IsEncrypted": false,
|
||||||
|
"Values": {}}`
|
||||||
|
);
|
||||||
|
|
||||||
|
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('testConnectionStringName'); // enter connection string setting name
|
||||||
|
|
||||||
|
// password prompt stub
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves((constants.yesString) as any);
|
||||||
|
// getConnectionString stubs - in password prompt logic
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials(); // create test connectionInfo
|
||||||
|
let connectionDetails = { options: connectionInfo };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
|
sinon.stub(azureFunctionsUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'testConnectionStringName', 'testConnectionString').resolves((true));
|
||||||
|
// addSqlNugetReferenceToProjectFile stub
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
let result = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(fileUri, connectionInfo);
|
||||||
|
|
||||||
|
should(inputBoxStub.calledOnce).be.true('showInputBox should have been called');
|
||||||
|
should(quickPickStub.callCount).be.equal(1, 'quickPickStub should have been called');
|
||||||
|
should(result?.connectionStringSettingName).be.equal('testConnectionStringName', 'Should return testConnectionStringName from manually entered connection string name');
|
||||||
|
should(result?.connectionInfo).be.equal(connectionInfo, 'Should return connectionInfo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(function (): void {
|
afterEach(function (): void {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ const fileUri = vscode.Uri.file('testUri');
|
|||||||
describe('Add SQL Binding quick pick', () => {
|
describe('Add SQL Binding quick pick', () => {
|
||||||
beforeEach(function (): void {
|
beforeEach(function (): void {
|
||||||
testUtils = createTestUtils();
|
testUtils = createTestUtils();
|
||||||
|
// create fake connection string settings for local.setting.json to be used
|
||||||
|
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
||||||
|
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
||||||
|
`{"IsEncrypted": false,
|
||||||
|
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function (): void {
|
afterEach(function (): void {
|
||||||
@@ -64,7 +70,7 @@ describe('Add SQL Binding quick pick', () => {
|
|||||||
// select input or output binding
|
// select input or output binding
|
||||||
quickpickStub.onSecondCall().resolves(<any>{ label: constants.input, type: BindingType.input });
|
quickpickStub.onSecondCall().resolves(<any>{ label: constants.input, type: BindingType.input });
|
||||||
sinon.stub(azureFunctionUtils, 'getAFProjectContainingFile').resolves(vscode.Uri.file('testUri'));
|
sinon.stub(azureFunctionUtils, 'getAFProjectContainingFile').resolves(vscode.Uri.file('testUri'));
|
||||||
// select connection profile - create new
|
// select connection string setting method - create new
|
||||||
quickpickStub.onThirdCall().resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
quickpickStub.onThirdCall().resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
||||||
// give connection string setting name
|
// give connection string setting name
|
||||||
sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('sqlConnectionString');
|
sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('sqlConnectionString');
|
||||||
@@ -189,7 +195,7 @@ describe('Add SQL Binding quick pick', () => {
|
|||||||
await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'));
|
await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'));
|
||||||
|
|
||||||
// should go back to the select connection string methods
|
// should go back to the select connection string methods
|
||||||
should(quickpickStub.callCount).be.equal(5,'showQuickPick should have been called 5 times');
|
should(quickpickStub.callCount).be.equal(5, 'showQuickPick should have been called 5 times');
|
||||||
should(quickpickStub.getCall(3).args).deepEqual([
|
should(quickpickStub.getCall(3).args).deepEqual([
|
||||||
[constants.connectionProfile, constants.userConnectionString],
|
[constants.connectionProfile, constants.userConnectionString],
|
||||||
{
|
{
|
||||||
@@ -225,7 +231,7 @@ describe('Add SQL Binding quick pick', () => {
|
|||||||
await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'));
|
await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'));
|
||||||
|
|
||||||
// should go back to the select connection string methods
|
// should go back to the select connection string methods
|
||||||
should(quickpickStub.callCount).be.equal(4,'showQuickPick should have been called 4 times');
|
should(quickpickStub.callCount).be.equal(4, 'showQuickPick should have been called 4 times');
|
||||||
should(quickpickStub.getCall(2).args).containDeepOrdered([
|
should(quickpickStub.getCall(2).args).containDeepOrdered([
|
||||||
[{ label: constants.createNewLocalAppSettingWithIcon }],
|
[{ label: constants.createNewLocalAppSettingWithIcon }],
|
||||||
{
|
{
|
||||||
@@ -267,7 +273,7 @@ describe('Add SQL Binding quick pick', () => {
|
|||||||
await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'));
|
await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'));
|
||||||
|
|
||||||
// should go back to the select connection string methods
|
// should go back to the select connection string methods
|
||||||
should(quickpickStub.callCount).be.equal(5,'showQuickPick should have been called 5 times');
|
should(quickpickStub.callCount).be.equal(5, 'showQuickPick should have been called 5 times');
|
||||||
should(quickpickStub.getCall(4).args).containDeepOrdered([
|
should(quickpickStub.getCall(4).args).containDeepOrdered([
|
||||||
[constants.connectionProfile, constants.enterConnectionString],
|
[constants.connectionProfile, constants.enterConnectionString],
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ describe('AzureFunctionsService', () => {
|
|||||||
beforeEach(function (): void {
|
beforeEach(function (): void {
|
||||||
testUtils = createTestUtils();
|
testUtils = createTestUtils();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Create Azure Function with SQL Binding', () => {
|
describe('Create Azure Function with SQL Binding', () => {
|
||||||
it('Should show info message to install azure functions extension if not installed', async function (): Promise<void> {
|
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);
|
const infoStub = sinon.stub(vscode.window, 'showInformationMessage').resolves(undefined);
|
||||||
@@ -41,7 +42,14 @@ describe('AzureFunctionsService', () => {
|
|||||||
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
||||||
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
|
||||||
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
// create fake connection string settings for local.setting.json to be used
|
||||||
|
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
||||||
|
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
||||||
|
`{"IsEncrypted": false,
|
||||||
|
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
||||||
|
);
|
||||||
|
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials(); // create test connectionInfo
|
||||||
|
|
||||||
let connectionDetails = { options: connectionInfo };
|
let connectionDetails = { options: connectionInfo };
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
@@ -96,6 +104,13 @@ describe('AzureFunctionsService', () => {
|
|||||||
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
||||||
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
|
||||||
|
// create fake connection string settings for local.setting.json to be used
|
||||||
|
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
||||||
|
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
||||||
|
`{"IsEncrypted": false,
|
||||||
|
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
||||||
|
);
|
||||||
|
|
||||||
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
||||||
let connectionDetails = { options: connectionInfo };
|
let connectionDetails = { options: connectionInfo };
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|||||||
Reference in New Issue
Block a user