mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Arc - Unit tests for deleting Postgres (#14502)
* tests for deleting postgres from overview page * upgrade to azdata-test 1.5.0 * Fix api stubbing Co-authored-by: Brian Bergeron <brberger@microsoft.com> Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
@@ -10,67 +10,78 @@ import * as azdataExt from 'azdata-ext';
|
||||
*/
|
||||
export class FakeAzdataApi implements azdataExt.IAzdataApi {
|
||||
|
||||
public postgresInstances: azdataExt.PostgresServerListResult[] = [];
|
||||
public miaaInstances: azdataExt.SqlMiListResult[] = [];
|
||||
private _arcApi = {
|
||||
dc: {
|
||||
create(_namespace: string, _name: string, _connectivityMode: string, _resourceGroup: string, _location: string, _subscription: string, _profileName?: string, _storageClass?: string): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); },
|
||||
endpoint: {
|
||||
async list(): Promise<azdataExt.AzdataOutput<azdataExt.DcEndpointListResult[]>> { return <any>{ result: [] }; }
|
||||
},
|
||||
config: {
|
||||
list(): Promise<azdataExt.AzdataOutput<azdataExt.DcConfigListResult[]>> { throw new Error('Method not implemented.'); },
|
||||
async show(): Promise<azdataExt.AzdataOutput<azdataExt.DcConfigShowResult>> { return <any>{ result: undefined! }; }
|
||||
}
|
||||
},
|
||||
postgres: {
|
||||
server: {
|
||||
postgresInstances: [],
|
||||
delete(_name: string): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); },
|
||||
async list(): Promise<azdataExt.AzdataOutput<azdataExt.PostgresServerListResult[]>> { return <any>{ result: this.postgresInstances }; },
|
||||
show(_name: string): Promise<azdataExt.AzdataOutput<azdataExt.PostgresServerShowResult>> { throw new Error('Method not implemented.'); },
|
||||
edit(
|
||||
_name: string,
|
||||
_args: {
|
||||
adminPassword?: boolean,
|
||||
coresLimit?: string,
|
||||
coresRequest?: string,
|
||||
engineSettings?: string,
|
||||
extensions?: string,
|
||||
memoryLimit?: string,
|
||||
memoryRequest?: string,
|
||||
noWait?: boolean,
|
||||
port?: number,
|
||||
replaceEngineSettings?: boolean,
|
||||
workers?: number
|
||||
},
|
||||
_engineVersion?: string,
|
||||
_additionalEnvVars?: azdataExt.AdditionalEnvVars
|
||||
): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
},
|
||||
sql: {
|
||||
mi: {
|
||||
miaaInstances: [],
|
||||
delete(_name: string): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); },
|
||||
async list(): Promise<azdataExt.AzdataOutput<azdataExt.SqlMiListResult[]>> { return <any>{ result: this.miaaInstances }; },
|
||||
show(_name: string): Promise<azdataExt.AzdataOutput<azdataExt.SqlMiShowResult>> { throw new Error('Method not implemented.'); },
|
||||
edit(
|
||||
_name: string,
|
||||
_args: {
|
||||
coresLimit?: string,
|
||||
coresRequest?: string,
|
||||
memoryLimit?: string,
|
||||
memoryRequest?: string,
|
||||
noWait?: boolean
|
||||
}): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// public postgresInstances: azdataExt.PostgresServerListResult[] = [];
|
||||
public set postgresInstances(instances: azdataExt.PostgresServerListResult[]) {
|
||||
this._arcApi.postgres.server.postgresInstances = <any>instances;
|
||||
}
|
||||
|
||||
public set miaaInstances(instances: azdataExt.SqlMiListResult[]) {
|
||||
this._arcApi.sql.mi.miaaInstances = <any>instances;
|
||||
}
|
||||
|
||||
// public miaaInstances: azdataExt.SqlMiListResult[] = [];
|
||||
|
||||
//
|
||||
// API Implementation
|
||||
//
|
||||
public get arc() {
|
||||
const self = this;
|
||||
return {
|
||||
dc: {
|
||||
create(_namespace: string, _name: string, _connectivityMode: string, _resourceGroup: string, _location: string, _subscription: string, _profileName?: string, _storageClass?: string): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); },
|
||||
endpoint: {
|
||||
async list(): Promise<azdataExt.AzdataOutput<azdataExt.DcEndpointListResult[]>> { return <any>{ result: [] }; }
|
||||
},
|
||||
config: {
|
||||
list(): Promise<azdataExt.AzdataOutput<azdataExt.DcConfigListResult[]>> { throw new Error('Method not implemented.'); },
|
||||
async show(): Promise<azdataExt.AzdataOutput<azdataExt.DcConfigShowResult>> { return <any>{ result: undefined! }; }
|
||||
}
|
||||
},
|
||||
postgres: {
|
||||
server: {
|
||||
delete(_name: string): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); },
|
||||
async list(): Promise<azdataExt.AzdataOutput<azdataExt.PostgresServerListResult[]>> { return <any>{ result: self.postgresInstances }; },
|
||||
show(_name: string): Promise<azdataExt.AzdataOutput<azdataExt.PostgresServerShowResult>> { throw new Error('Method not implemented.'); },
|
||||
edit(
|
||||
_name: string,
|
||||
_args: {
|
||||
adminPassword?: boolean,
|
||||
coresLimit?: string,
|
||||
coresRequest?: string,
|
||||
engineSettings?: string,
|
||||
extensions?: string,
|
||||
memoryLimit?: string,
|
||||
memoryRequest?: string,
|
||||
noWait?: boolean,
|
||||
port?: number,
|
||||
replaceEngineSettings?: boolean,
|
||||
workers?: number
|
||||
},
|
||||
_engineVersion?: string,
|
||||
_additionalEnvVars?: azdataExt.AdditionalEnvVars
|
||||
): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
},
|
||||
sql: {
|
||||
mi: {
|
||||
delete(_name: string): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); },
|
||||
async list(): Promise<azdataExt.AzdataOutput<azdataExt.SqlMiListResult[]>> { return <any>{ result: self.miaaInstances }; },
|
||||
show(_name: string): Promise<azdataExt.AzdataOutput<azdataExt.SqlMiShowResult>> { throw new Error('Method not implemented.'); },
|
||||
edit(
|
||||
_name: string,
|
||||
_args: {
|
||||
coresLimit?: string,
|
||||
coresRequest?: string,
|
||||
memoryLimit?: string,
|
||||
memoryRequest?: string,
|
||||
noWait?: boolean
|
||||
}): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
}
|
||||
};
|
||||
return this._arcApi;
|
||||
}
|
||||
getPath(): Promise<string> {
|
||||
throw new Error('Method not implemented.');
|
||||
|
||||
@@ -18,7 +18,6 @@ import { ConnectToPGSqlDialog } from '../../ui/dialogs/connectPGDialog';
|
||||
import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
|
||||
import { FakeControllerModel } from '../mocks/fakeControllerModel';
|
||||
import { FakeAzdataApi } from '../mocks/fakeAzdataApi';
|
||||
import { assert } from 'sinon';
|
||||
|
||||
export const FakePostgresServerShowOutput: azdataExt.AzdataOutput<azdataExt.PostgresServerShowResult> = {
|
||||
logs: [],
|
||||
@@ -134,46 +133,34 @@ describe('PostgresModel', function (): void {
|
||||
});
|
||||
|
||||
it('Updates model to expected config', async function (): Promise<void> {
|
||||
const postgresShow = sinon.stub().returns(FakePostgresServerShowOutput);
|
||||
sinon.stub(azdataApi, 'arc').get(() => {
|
||||
return { postgres: { server: { show(name: string) { return postgresShow(name); } } } };
|
||||
});
|
||||
const postgresShowStub = sinon.stub(azdataApi.arc.postgres.server, 'show').resolves(FakePostgresServerShowOutput);
|
||||
|
||||
await postgresModel.refresh();
|
||||
sinon.assert.calledOnceWithExactly(postgresShow, postgresModel.info.name);
|
||||
assert.match(postgresModel.config, FakePostgresServerShowOutput.result);
|
||||
sinon.assert.calledOnceWithExactly(postgresShowStub, postgresModel.info.name, sinon.match.any, sinon.match.any);
|
||||
sinon.assert.match(postgresModel.config, FakePostgresServerShowOutput.result);
|
||||
});
|
||||
|
||||
it('Updates onConfigLastUpdated when model is refreshed', async function (): Promise<void> {
|
||||
const postgresShow = sinon.stub().returns(FakePostgresServerShowOutput);
|
||||
sinon.stub(azdataApi, 'arc').get(() => {
|
||||
return { postgres: { server: { show(name: string) { return postgresShow(name); } } } };
|
||||
});
|
||||
const postgresShowStub = sinon.stub(azdataApi.arc.postgres.server, 'show').resolves(FakePostgresServerShowOutput);
|
||||
|
||||
await postgresModel.refresh();
|
||||
sinon.assert.calledOnceWithExactly(postgresShow, postgresModel.info.name);
|
||||
sinon.assert.calledOnceWithExactly(postgresShowStub, postgresModel.info.name, sinon.match.any, sinon.match.any);
|
||||
should(postgresModel.configLastUpdated).be.Date();
|
||||
});
|
||||
|
||||
it('Calls onConfigUpdated event when model is refreshed', async function (): Promise<void> {
|
||||
const postgresShow = sinon.stub().returns(FakePostgresServerShowOutput);
|
||||
sinon.stub(azdataApi, 'arc').get(() => {
|
||||
return { postgres: { server: { show(name: string) { return postgresShow(name); } } } };
|
||||
});
|
||||
const postgresShowStub = sinon.stub(azdataApi.arc.postgres.server, 'show').resolves(FakePostgresServerShowOutput);
|
||||
const configUpdatedEvent = sinon.spy(vscode.EventEmitter.prototype, 'fire');
|
||||
|
||||
await postgresModel.refresh();
|
||||
sinon.assert.calledOnceWithExactly(postgresShow, postgresModel.info.name);
|
||||
sinon.assert.calledOnceWithExactly(postgresShowStub, postgresModel.info.name, sinon.match.any, sinon.match.any);
|
||||
sinon.assert.calledOnceWithExactly(configUpdatedEvent, postgresModel.config);
|
||||
});
|
||||
|
||||
it('Expected exception is thrown', async function (): Promise<void> {
|
||||
// Stub 'azdata arc postgres server show' to throw an exception
|
||||
const error = new Error("something bad happened");
|
||||
const postgresShow = sinon.stub().throws(error);
|
||||
sinon.stub(azdataApi, 'arc').get(() => {
|
||||
return { postgres: { server: { show(name: string) { return postgresShow(name); } } } };
|
||||
});
|
||||
const error = new Error('something bad happened');
|
||||
sinon.stub(azdataApi.arc.postgres.server, 'show').throws(error);
|
||||
|
||||
await should(postgresModel.refresh()).be.rejectedWith(error);
|
||||
});
|
||||
@@ -187,11 +174,7 @@ describe('PostgresModel', function (): void {
|
||||
const registration: Registration = { instanceName: '', state: '', instanceType: ResourceType.postgresInstances };
|
||||
postgresModel = new PostgresModel(controllerModel, postgresResource, registration, new AzureArcTreeDataProvider(TypeMoq.Mock.ofType<vscode.ExtensionContext>().object));
|
||||
|
||||
//Stub calling refresh postgres model
|
||||
const postgresShow = sinon.stub().returns(FakePostgresServerShowOutput);
|
||||
sinon.stub(azdataApi, 'arc').get(() => {
|
||||
return { postgres: { server: { show(name: string) { return postgresShow(name); } } } };
|
||||
});
|
||||
sinon.stub(azdataApi.arc.postgres.server, 'show').resolves(FakePostgresServerShowOutput);
|
||||
|
||||
//Call to provide external endpoint
|
||||
await postgresModel.refresh();
|
||||
@@ -360,10 +343,7 @@ describe('PostgresModel', function (): void {
|
||||
postgresModel = new PostgresModel(controllerModel, postgresResource, registration, new AzureArcTreeDataProvider(TypeMoq.Mock.ofType<vscode.ExtensionContext>().object));
|
||||
|
||||
//Stub calling refresh postgres model
|
||||
const postgresShow = sinon.stub().returns(FakePostgresServerShowOutput);
|
||||
sinon.stub(azdataApi, 'arc').get(() => {
|
||||
return { postgres: { server: { show(name: string) { return postgresShow(name); } } } };
|
||||
});
|
||||
sinon.stub(azdataApi.arc.postgres.server, 'show').resolves(FakePostgresServerShowOutput);
|
||||
|
||||
//Stub how to get connection profile
|
||||
const iconnectionProfileMock = TypeMoq.Mock.ofType<azdata.IConnectionProfile>();
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as sinon from 'sinon';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as azdataExt from 'azdata-ext';
|
||||
import * as utils from '../../../common/utils';
|
||||
import * as loc from '../../../localizedConstants';
|
||||
import { Deferred } from '../../../common/promise';
|
||||
import { createModelViewMock } from '@microsoft/azdata-test/out/mocks/modelView/modelViewMock';
|
||||
import { StubButton } from '@microsoft/azdata-test/out/stubs/modelView/stubButton';
|
||||
import { PGResourceInfo, ResourceType } from 'arc';
|
||||
import { PostgresOverviewPage } from '../../../ui/dashboards/postgres/postgresOverviewPage';
|
||||
import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
||||
import { FakeControllerModel } from '../../mocks/fakeControllerModel';
|
||||
import { FakeAzdataApi } from '../../mocks/fakeAzdataApi';
|
||||
import { PostgresModel } from '../../../models/postgresModel';
|
||||
import { ControllerModel, Registration } from '../../../models/controllerModel';
|
||||
|
||||
describe('postgresOverviewPage', () => {
|
||||
let postgresOverview: PostgresOverviewPage;
|
||||
let azdataApi: azdataExt.IAzdataApi;
|
||||
let controllerModel: ControllerModel;
|
||||
let postgresModel: PostgresModel;
|
||||
|
||||
let showInformationMessage: sinon.SinonStub;
|
||||
let showErrorMessage: sinon.SinonStub;
|
||||
|
||||
let informationMessageShown: Deferred;
|
||||
let errorMessageShown: Deferred;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Stub the azdata CLI API
|
||||
azdataApi = new FakeAzdataApi();
|
||||
const azdataExt = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
||||
azdataExt.setup(x => x.azdata).returns(() => azdataApi);
|
||||
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExt.object });
|
||||
|
||||
// Stub the window UI
|
||||
informationMessageShown = new Deferred();
|
||||
showInformationMessage = sinon.stub(vscode.window, 'showInformationMessage').callsFake(
|
||||
(_: string, __: vscode.MessageOptions, ...___: vscode.MessageItem[]) => {
|
||||
informationMessageShown.resolve();
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
|
||||
errorMessageShown = new Deferred();
|
||||
showErrorMessage = sinon.stub(vscode.window, 'showErrorMessage').callsFake(
|
||||
(_: string, __: vscode.MessageOptions, ...___: vscode.MessageItem[]) => {
|
||||
errorMessageShown.resolve();
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
|
||||
// Setup the PostgresModel
|
||||
controllerModel = new FakeControllerModel();
|
||||
const postgresResource: PGResourceInfo = { name: 'my-pg', resourceType: '' };
|
||||
const registration: Registration = { instanceName: '', state: '', instanceType: ResourceType.postgresInstances };
|
||||
const treeDataProvider = new AzureArcTreeDataProvider(TypeMoq.Mock.ofType<vscode.ExtensionContext>().object);
|
||||
postgresModel = new PostgresModel(controllerModel, postgresResource, registration, treeDataProvider);
|
||||
|
||||
// Setup the PostgresOverviewPage
|
||||
const { modelViewMock } = createModelViewMock();
|
||||
postgresOverview = new PostgresOverviewPage(modelViewMock.object, controllerModel, postgresModel);
|
||||
// Call the getter to initialize toolbar, but we don't need to use it for anything
|
||||
// eslint-disable-next-line code-no-unused-expressions
|
||||
postgresOverview['toolbarContainer'];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
describe('delete button', () => {
|
||||
let refreshTreeNode: sinon.SinonStub;
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(utils, 'promptForInstanceDeletion').returns(Promise.resolve(true));
|
||||
sinon.stub(controllerModel, 'acquireAzdataSession').returns(Promise.resolve(vscode.Disposable.from()));
|
||||
refreshTreeNode = sinon.stub(controllerModel, 'refreshTreeNode');
|
||||
});
|
||||
|
||||
it('deletes Postgres on success', async () => {
|
||||
// Stub 'azdata arc postgres server delete' to return success
|
||||
const postgresDeleteStub = sinon.stub(azdataApi.arc.postgres.server, 'delete');
|
||||
|
||||
(postgresOverview['deleteButton'] as StubButton).click();
|
||||
await informationMessageShown;
|
||||
sinon.assert.calledOnceWithExactly(postgresDeleteStub, postgresModel.info.name, sinon.match.any, sinon.match.any);
|
||||
sinon.assert.calledOnceWithExactly(showInformationMessage, loc.instanceDeleted(postgresModel.info.name));
|
||||
sinon.assert.notCalled(showErrorMessage);
|
||||
sinon.assert.calledOnce(refreshTreeNode);
|
||||
});
|
||||
|
||||
it('shows an error message on failure', async () => {
|
||||
// Stub 'azdata arc postgres server delete' to throw an exception
|
||||
const error = new Error('something bad happened');
|
||||
const postgresDeleteStub = sinon.stub(azdataApi.arc.postgres.server, 'delete').throws(error);
|
||||
|
||||
(postgresOverview['deleteButton'] as StubButton).click();
|
||||
await errorMessageShown;
|
||||
sinon.assert.calledOnceWithExactly(postgresDeleteStub, postgresModel.info.name, sinon.match.any, sinon.match.any);
|
||||
sinon.assert.notCalled(showInformationMessage);
|
||||
sinon.assert.calledOnceWithExactly(showErrorMessage, loc.instanceDeletionFailed(postgresModel.info.name, error.message));
|
||||
sinon.assert.notCalled(refreshTreeNode);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -102,8 +102,10 @@ describe('AzureArcTreeDataProvider tests', function (): void {
|
||||
return mockArcApi.object;
|
||||
});
|
||||
const fakeAzdataApi = new FakeAzdataApi();
|
||||
fakeAzdataApi.postgresInstances = [{ name: 'pg1', state: '', workers: 0 }];
|
||||
fakeAzdataApi.miaaInstances = [{ name: 'miaa1', state: '', replicas: '', serverEndpoint: '' }];
|
||||
const pgInstances = [{ name: 'pg1', state: '', workers: 0 }];
|
||||
const miaaInstances = [{ name: 'miaa1', state: '', replicas: '', serverEndpoint: '' }];
|
||||
fakeAzdataApi.postgresInstances = pgInstances;
|
||||
fakeAzdataApi.miaaInstances = miaaInstances;
|
||||
mockArcApi.setup(x => x.azdata).returns(() => fakeAzdataApi);
|
||||
|
||||
sinon.stub(vscode.extensions, 'getExtension').returns(mockArcExtension.object);
|
||||
@@ -112,8 +114,8 @@ describe('AzureArcTreeDataProvider tests', function (): void {
|
||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||
const controllerNode = treeDataProvider.getControllerNode(controllerModel);
|
||||
const children = await treeDataProvider.getChildren(controllerNode);
|
||||
should(children.filter(c => c.label === fakeAzdataApi.postgresInstances[0].name).length).equal(1, 'Should have a Postgres child');
|
||||
should(children.filter(c => c.label === fakeAzdataApi.miaaInstances[0].name).length).equal(1, 'Should have a MIAA child');
|
||||
should(children.filter(c => c.label === pgInstances[0].name).length).equal(1, 'Should have a Postgres child');
|
||||
should(children.filter(c => c.label === miaaInstances[0].name).length).equal(1, 'Should have a MIAA child');
|
||||
should(children.length).equal(2, 'Should have exactly 2 children');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user