Fix arc node info updating (#12007)

* Fix arc node info updating

* Missed await
This commit is contained in:
Charles Gagnon
2020-08-28 15:53:17 -07:00
committed by GitHub
parent ceb4df5b8b
commit 47c1204e89
12 changed files with 179 additions and 50 deletions

View File

@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { v4 as uuid } from 'uuid';
import { ControllerModel, ControllerInfo } from '../../models/controllerModel';
import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
export class FakeControllerModel extends ControllerModel {
constructor(treeDataProvider?: AzureArcTreeDataProvider, info?: Partial<ControllerInfo>, password?: string) {
const _info: ControllerInfo = Object.assign({ id: uuid(), url: '', name: '', username: '', rememberPassword: false, resources: [] }, info);
super(treeDataProvider!, _info, password);
}
}

View File

@@ -9,8 +9,9 @@ import * as sinon from 'sinon';
import * as TypeMoq from 'typemoq';
import * as vscode from 'vscode';
import * as should from 'should';
import { v4 as uuid } from 'uuid';
import { ConnectToControllerDialog } from '../../ui/dialogs/connectControllerDialog';
import { ControllerModel } from '../../models/controllerModel';
import { ControllerModel, ControllerInfo } from '../../models/controllerModel';
import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
import { UserCancelledError } from '../../common/utils';
@@ -36,7 +37,7 @@ describe('ControllerModel', function (): void {
it('Rejected with expected error when user cancels', async function (): Promise<void> {
// Returning an undefined model here indicates that the dialog closed without clicking "Ok" - usually through the user clicking "Cancel"
sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve(undefined));
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
await should(model.azdataLogin()).be.rejectedWith(new UserCancelledError());
});
@@ -55,7 +56,7 @@ describe('ControllerModel', function (): void {
azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
azdataExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
await model.azdataLogin();
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password), TypeMoq.Times.once());
@@ -78,10 +79,10 @@ describe('ControllerModel', function (): void {
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
// Set up dialog to return new model with our password
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
await model.azdataLogin();
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password), TypeMoq.Times.once());
@@ -103,10 +104,10 @@ describe('ControllerModel', function (): void {
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
// Set up dialog to return new model with our new password from the reprompt
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
await model.azdataLogin(true);
should(waitForCloseStub.called).be.true('waitForClose should have been called');
@@ -129,16 +130,72 @@ describe('ControllerModel', function (): void {
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
// Set up dialog to return new model with our new password from the reprompt
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
// Set up original model with a password
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, 'originalPassword');
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, 'originalPassword');
await model.azdataLogin(true);
should(waitForCloseStub.called).be.true('waitForClose should have been called');
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password), TypeMoq.Times.once());
});
it('Model values are updated correctly when modified during reconnect', async function (): Promise<void> {
const treeDataProvider = new AzureArcTreeDataProvider(mockExtensionContext.object);
// Set up cred store to return a password to start with
const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: 'originalPassword' }));
// Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
credProviderMock.setup((x: any) => x.then).returns(() => undefined);
sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
const azdataExtApiMock = TypeMoq.Mock.ofType<azdataExt.IExtension>();
const azdataMock = TypeMoq.Mock.ofType<azdataExt.IAzdataApi>();
azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
azdataExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
// Add existing model to provider
const originalPassword = 'originalPassword';
const model = new ControllerModel(
treeDataProvider,
{
id: uuid(),
url: '127.0.0.1',
username: 'admin',
name: 'arc',
rememberPassword: false,
resources: []
},
originalPassword
);
await treeDataProvider.addOrUpdateController(model, originalPassword);
const newInfo: ControllerInfo = {
id: model.info.id, // The ID stays the same since we're just re-entering information for the same model
url: 'newUrl',
username: 'newUser',
name: 'newName',
rememberPassword: true,
resources: []
};
const newPassword = 'newPassword';
// Set up dialog to return new model with our new password from the reprompt
const newModel = new ControllerModel(
treeDataProvider,
newInfo,
newPassword);
const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve(
{ controllerModel: newModel, password: newPassword }));
await model.azdataLogin(true);
should(waitForCloseStub.called).be.true('waitForClose should have been called');
should((await treeDataProvider.getChildren()).length).equal(1, 'Tree Data provider should still only have 1 node');
should(model.info).deepEqual(newInfo, 'Model info should have been updated');
});
});
});

View File

@@ -8,6 +8,7 @@ import * as sinon from 'sinon';
import { ControllerInfo, ControllerModel } from '../../../models/controllerModel';
import { ConnectToControllerDialog } from '../../../ui/dialogs/connectControllerDialog';
import * as loc from '../../../localizedConstants';
import { v4 as uuid } from 'uuid';
describe('ConnectControllerDialog', function (): void {
afterEach(function (): void {
@@ -30,7 +31,7 @@ describe('ConnectControllerDialog', function (): void {
it('validate returns false if controller refresh fails', async function (): Promise<void> {
sinon.stub(ControllerModel.prototype, 'refresh').returns(Promise.reject('Controller refresh failed'));
const connectControllerDialog = new ConnectToControllerDialog(undefined!);
const info = { url: 'https://127.0.0.1:30080', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] };
const info = { id: uuid(), url: 'https://127.0.0.1:30080', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] };
connectControllerDialog.showDialog(info, 'pwd');
await connectControllerDialog.isInitialized;
const validateResult = await connectControllerDialog.validate();
@@ -39,36 +40,36 @@ describe('ConnectControllerDialog', function (): void {
it('validate replaces http with https', async function (): Promise<void> {
await validateConnectControllerDialog(
{ url: 'http://127.0.0.1:30081', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
{ id: uuid(), url: 'http://127.0.0.1:30081', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30081');
});
it('validate appends https if missing', async function (): Promise<void> {
await validateConnectControllerDialog({ url: '127.0.0.1:30080', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
await validateConnectControllerDialog({ id: uuid(), url: '127.0.0.1:30080', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30080');
});
it('validate appends default port if missing', async function (): Promise<void> {
await validateConnectControllerDialog({ url: 'https://127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
await validateConnectControllerDialog({ id: uuid(), url: 'https://127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30080');
});
it('validate appends both port and https if missing', async function (): Promise<void> {
await validateConnectControllerDialog({ url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
await validateConnectControllerDialog({ id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30080');
});
for (const name of ['', undefined]) {
it.skip(`validate display name gets set to arc instance name for user chosen name of:${name}`, async function (): Promise<void> {
await validateConnectControllerDialog(
{ url: 'http://127.0.0.1:30081', name: name!, username: 'sa', rememberPassword: true, resources: [] },
{ id: uuid(), url: 'http://127.0.0.1:30081', name: name!, username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30081');
});
}
it.skip(`validate display name gets set to default data controller name for user chosen name of:'' and instanceName in explicably returned as undefined from the controller endpoint`, async function (): Promise<void> {
await validateConnectControllerDialog(
{ url: 'http://127.0.0.1:30081', name: '', username: 'sa', rememberPassword: true, resources: [] },
{ id: uuid(), url: 'http://127.0.0.1:30081', name: '', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30081',
undefined);
});

View File

@@ -6,10 +6,12 @@
import 'mocha';
import * as should from 'should';
import * as TypeMoq from 'typemoq';
import { v4 as uuid } from 'uuid';
import * as vscode from 'vscode';
import { ControllerModel } from '../../../models/controllerModel';
import { ControllerModel, ControllerInfo } from '../../../models/controllerModel';
import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
import { ControllerTreeNode } from '../../../ui/tree/controllerTreeNode';
import { FakeControllerModel } from '../../mocks/fakeControllerModel';
describe('AzureArcTreeDataProvider tests', function (): void {
let treeDataProvider: AzureArcTreeDataProvider;
@@ -27,15 +29,17 @@ describe('AzureArcTreeDataProvider tests', function (): void {
treeDataProvider['_loading'] = false;
let children = await treeDataProvider.getChildren();
should(children.length).equal(0, 'There initially shouldn\'t be any children');
const controllerModelMock = TypeMoq.Mock.ofType<ControllerModel>();
await treeDataProvider.addOrUpdateController(controllerModelMock.object, '');
const controllerModel = new FakeControllerModel();
await treeDataProvider.addOrUpdateController(controllerModel, '');
children = await treeDataProvider.getChildren();
should(children.length).equal(1, 'Controller node should be added correctly');
// Add a couple more
const controllerModelMock2 = TypeMoq.Mock.ofType<ControllerModel>();
const controllerModelMock3 = TypeMoq.Mock.ofType<ControllerModel>();
await treeDataProvider.addOrUpdateController(controllerModelMock2.object, '');
await treeDataProvider.addOrUpdateController(controllerModelMock3.object, '');
const controllerModel2 = new FakeControllerModel();
const controllerModel3 = new FakeControllerModel();
await treeDataProvider.addOrUpdateController(controllerModel2, '');
await treeDataProvider.addOrUpdateController(controllerModel3, '');
children = await treeDataProvider.getChildren();
should(children.length).equal(3, 'Additional Controller nodes should be added correctly');
});
@@ -43,7 +47,7 @@ describe('AzureArcTreeDataProvider tests', function (): void {
treeDataProvider['_loading'] = false;
let children = await treeDataProvider.getChildren();
should(children.length).equal(0, 'There initially shouldn\'t be any children');
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
const controllerModel = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
await treeDataProvider.addOrUpdateController(controllerModel, '');
should(children.length).equal(1, 'Controller node should be added correctly');
await treeDataProvider.addOrUpdateController(controllerModel, '');
@@ -54,14 +58,16 @@ describe('AzureArcTreeDataProvider tests', function (): void {
treeDataProvider['_loading'] = false;
let children = await treeDataProvider.getChildren();
should(children.length).equal(0, 'There initially shouldn\'t be any children');
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
const originalInfo: ControllerInfo = { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] };
const controllerModel = new ControllerModel(treeDataProvider, originalInfo);
await treeDataProvider.addOrUpdateController(controllerModel, '');
should(children.length).equal(1, 'Controller node should be added correctly');
should((<ControllerTreeNode>children[0]).model.info.rememberPassword).be.true('Info was not set correctly initially');
const controllerModel2 = new ControllerModel(treeDataProvider, { url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: false, resources: [] });
should((<ControllerTreeNode>children[0]).model.info).deepEqual(originalInfo);
const newInfo = { id: originalInfo.id, url: '1.1.1.1', name: 'new-name', username: 'admin', rememberPassword: false, resources: [] };
const controllerModel2 = new ControllerModel(treeDataProvider, newInfo);
await treeDataProvider.addOrUpdateController(controllerModel2, '');
should(children.length).equal(1, 'Shouldn\'t add duplicate controller node');
should((<ControllerTreeNode>children[0]).model.info.rememberPassword).be.false('Info was not updated correctly');
should((<ControllerTreeNode>children[0]).model.info).deepEqual(newInfo);
});
});
@@ -82,8 +88,8 @@ describe('AzureArcTreeDataProvider tests', function (): void {
describe('removeController', function (): void {
it('removing a controller should work as expected', async function (): Promise<void> {
treeDataProvider['_loading'] = false;
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
const controllerModel2 = new ControllerModel(treeDataProvider, { url: '127.0.0.2', name: 'my-arc', username: 'cloudsa', rememberPassword: true, resources: [] });
const controllerModel = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
const controllerModel2 = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.2', name: 'my-arc', username: 'cloudsa', rememberPassword: true, resources: [] });
await treeDataProvider.addOrUpdateController(controllerModel, '');
await treeDataProvider.addOrUpdateController(controllerModel2, '');
const children = <ControllerTreeNode[]>(await treeDataProvider.getChildren());