Add acquireLoginSession API for Azdata (#13985)

* wip

* fix tests

* add tests

* PR comments
This commit is contained in:
Charles Gagnon
2021-01-20 10:35:26 -08:00
committed by GitHub
parent 9e9fac2991
commit 04dff9cdf2
16 changed files with 488 additions and 188 deletions

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdataExt from 'azdata-ext';
import * as should from 'should';
import * as sinon from 'sinon';
import * as vscode from 'vscode';
@@ -16,6 +17,7 @@ import * as fs from 'fs';
import { AzdataReleaseInfo } from '../azdataReleaseInfo';
import * as TypeMoq from 'typemoq';
import { eulaAccepted } from '../constants';
import { sleep } from './testUtils';
const oldAzdataMock = new azdata.AzdataTool('/path/to/azdata', '0.0.0');
const currentAzdataMock = new azdata.AzdataTool('/path/to/azdata', '9999.999.999');
@@ -170,18 +172,6 @@ describe('azdata', function () {
});
});
});
it('login', async function (): Promise<void> {
const endpoint = 'myEndpoint';
const username = 'myUsername';
const password = 'myPassword';
await azdataTool.login(endpoint, username, password);
verifyExecuteCommandCalledWithArgs(['login', endpoint, username]);
});
it('version', async function (): Promise<void> {
executeCommandStub.resolves({ stdout: '1.0.0', stderr: '' });
await azdataTool.version();
verifyExecuteCommandCalledWithArgs(['--version']);
});
it('general error throws', async function (): Promise<void> {
const err = new Error();
executeCommandStub.throws(err);
@@ -228,12 +218,136 @@ describe('azdata', function () {
});
});
it('login', async function (): Promise<void> {
const endpoint = 'myEndpoint';
const username = 'myUsername';
const password = 'myPassword';
await azdataTool.login(endpoint, username, password);
verifyExecuteCommandCalledWithArgs(['login', endpoint, username]);
});
describe('acquireSession', function (): void {
it('calls login', async function (): Promise<void> {
const endpoint = 'myEndpoint';
const username = 'myUsername';
const password = 'myPassword';
const session = await azdataTool.acquireSession(endpoint, username, password);
session.dispose();
verifyExecuteCommandCalledWithArgs(['login', endpoint, username]);
});
it('command executed under current session completes', async function (): Promise<void> {
const session = await azdataTool.acquireSession('', '', '');
try {
await azdataTool.arc.dc.config.show(undefined, session);
} finally {
session.dispose();
}
verifyExecuteCommandCalledWithArgs(['login'], 0);
verifyExecuteCommandCalledWithArgs(['arc', 'dc', 'config', 'show'], 1);
});
it('multiple commands executed under current session completes', async function (): Promise<void> {
const session = await azdataTool.acquireSession('', '', '');
try {
// Kick off multiple commands at the same time and then ensure that they both complete
await Promise.all([
azdataTool.arc.dc.config.show(undefined, session),
azdataTool.arc.sql.mi.list(undefined, session)
]);
} finally {
session.dispose();
}
verifyExecuteCommandCalledWithArgs(['login'], 0);
verifyExecuteCommandCalledWithArgs(['arc', 'dc', 'config', 'show'], 1);
verifyExecuteCommandCalledWithArgs(['arc', 'sql', 'mi', 'list'], 2);
});
it('command executed without session context is queued up until session is closed', async function (): Promise<void> {
const session = await azdataTool.acquireSession('', '', '');
let nonSessionCommand: Promise<any> | undefined = undefined;
try {
// Start one command in the current session
await azdataTool.arc.dc.config.show(undefined, session);
// Verify that the command isn't executed until after the session is disposed
let isFulfilled = false;
nonSessionCommand = azdataTool.arc.sql.mi.list().then(() => isFulfilled = true);
await sleep(2000);
should(isFulfilled).equal(false, 'The command should not be completed yet');
} finally {
session.dispose();
}
await nonSessionCommand;
verifyExecuteCommandCalledWithArgs(['login'], 0);
verifyExecuteCommandCalledWithArgs(['arc', 'dc', 'config', 'show'], 1);
verifyExecuteCommandCalledWithArgs(['arc', 'sql', 'mi', 'list'], 2);
});
it('multiple commands executed without session context are queued up until session is closed', async function (): Promise<void> {
const session = await azdataTool.acquireSession('', '', '');
let nonSessionCommand1: Promise<any> | undefined = undefined;
let nonSessionCommand2: Promise<any> | undefined = undefined;
try {
// Start one command in the current session
await azdataTool.arc.dc.config.show(undefined, session);
// Verify that neither command is completed until the session is closed
let isFulfilled = false;
nonSessionCommand1 = azdataTool.arc.sql.mi.list().then(() => isFulfilled = true);
nonSessionCommand2 = azdataTool.arc.postgres.server.list().then(() => isFulfilled = true);
await sleep(2000);
should(isFulfilled).equal(false, 'The commands should not be completed yet');
} finally {
session.dispose();
}
await Promise.all([nonSessionCommand1, nonSessionCommand2]);
verifyExecuteCommandCalledWithArgs(['login'], 0);
verifyExecuteCommandCalledWithArgs(['arc', 'dc', 'config', 'show'], 1);
verifyExecuteCommandCalledWithArgs(['arc', 'sql', 'mi', 'list'], 2);
verifyExecuteCommandCalledWithArgs(['arc', 'postgres', 'server', 'list'], 3);
});
it('attempting to acquire a second session while a first is still active queues the second session', async function (): Promise<void> {
const firstSession = await azdataTool.acquireSession('', '', '');
let sessionPromise: Promise<azdataExt.AzdataSession> | undefined = undefined;
let secondSessionCommand: Promise<any> | undefined = undefined;
try {
try {
// Start one command in the current session
await azdataTool.arc.dc.config.show(undefined, firstSession);
// Verify that none of the commands for the second session are completed before the first is disposed
let isFulfilled = false;
sessionPromise = azdataTool.acquireSession('', '', '');
sessionPromise.then(session => {
isFulfilled = true;
secondSessionCommand = azdataTool.arc.sql.mi.list(undefined, session).then(() => isFulfilled = true);
});
await sleep(2000);
should(isFulfilled).equal(false, 'The commands should not be completed yet');
} finally {
firstSession.dispose();
}
} finally {
(await sessionPromise)?.dispose();
}
should(secondSessionCommand).not.equal(undefined, 'The second command should have been queued already');
await secondSessionCommand!;
verifyExecuteCommandCalledWithArgs(['login'], 0);
verifyExecuteCommandCalledWithArgs(['arc', 'dc', 'config', 'show'], 1);
verifyExecuteCommandCalledWithArgs(['login'], 2);
verifyExecuteCommandCalledWithArgs(['arc', 'sql', 'mi', 'list'], 3);
});
});
it('version', async function (): Promise<void> {
executeCommandStub.resolves({ stdout: '1.0.0', stderr: '' });
await azdataTool.version();
verifyExecuteCommandCalledWithArgs(['--version']);
});
/**
* Verifies that the specified args were included in the call to executeCommand
* @param args The args to check were included in the execute command call
*/
function verifyExecuteCommandCalledWithArgs(args: string[]): void {
const commandArgs = executeCommandStub.args[0][1] as string[];
function verifyExecuteCommandCalledWithArgs(args: string[], callIndex = 0): void {
const commandArgs = executeCommandStub.args[callIndex][1] as string[];
args.forEach(arg => should(commandArgs).containEql(arg));
}
@@ -469,8 +583,8 @@ describe('azdata', function () {
});
});
describe('promptForEula', function(): void {
it('skipped because of config', async function(): Promise<void> {
describe('promptForEula', function (): void {
it('skipped because of config', async function (): Promise<void> {
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.dontPrompt);
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
@@ -479,7 +593,7 @@ describe('azdata', function () {
should(result).be.false();
});
it('always prompt if user requested', async function(): Promise<void> {
it('always prompt if user requested', async function (): Promise<void> {
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.dontPrompt);
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
@@ -490,7 +604,7 @@ describe('azdata', function () {
should(showInformationMessage.calledOnce).be.true('showInformationMessage should have been called to prompt user');
});
it('prompt if config set to do so', async function(): Promise<void> {
it('prompt if config set to do so', async function (): Promise<void> {
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
@@ -501,7 +615,7 @@ describe('azdata', function () {
should(showInformationMessage.calledOnce).be.true('showInformationMessage should have been called to prompt user');
});
it('update config if user chooses not to prompt', async function(): Promise<void> {
it('update config if user chooses not to prompt', async function (): Promise<void> {
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
@@ -513,7 +627,7 @@ describe('azdata', function () {
should(showInformationMessage.calledOnce).be.true('showInformationMessage should have been called to prompt user');
});
it('user accepted EULA', async function(): Promise<void> {
it('user accepted EULA', async function (): Promise<void> {
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
@@ -525,7 +639,7 @@ describe('azdata', function () {
should(showInformationMessage.calledOnce).be.true('showInformationMessage should have been called to prompt user');
});
it('user accepted EULA - require user action', async function(): Promise<void> {
it('user accepted EULA - require user action', async function (): Promise<void> {
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
@@ -538,7 +652,7 @@ describe('azdata', function () {
});
});
describe('isEulaAccepted', function(): void {
describe('isEulaAccepted', function (): void {
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
mementoMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => true);
should(azdata.isEulaAccepted(mementoMock.object)).be.true();