mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-04 09:35:38 -05:00
Add arc tree data provider tests (#12758)
* Add arc tree data provider tests * Generic ResourceModel * no message * undo * Fix compile error
This commit is contained in:
77
extensions/arc/src/test/mocks/fakeAzdataApi.ts
Normal file
77
extensions/arc/src/test/mocks/fakeAzdataApi.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as azdataExt from 'azdata-ext';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple fake Azdata Api used to mock the API during tests
|
||||||
|
*/
|
||||||
|
export class FakeAzdataApi implements azdataExt.IAzdataApi {
|
||||||
|
|
||||||
|
public postgresInstances: azdataExt.PostgresServerListResult[] = [];
|
||||||
|
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
|
||||||
|
},
|
||||||
|
_additionalEnvVars?: { [key: string]: string }): 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.'); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
getPath(): Promise<string> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
login(_endpoint: string, _username: string, _password: string): Promise<azdataExt.AzdataOutput<any>> {
|
||||||
|
return <any>undefined;
|
||||||
|
}
|
||||||
|
version(): Promise<azdataExt.AzdataOutput<string>> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
getSemVersion(): any {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,16 +3,21 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ControllerInfo } from 'arc';
|
import { ControllerInfo, ResourceType } from 'arc';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
|
import * as sinon from 'sinon';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
import * as azdataExt from 'azdata-ext';
|
||||||
import { ControllerModel } from '../../../models/controllerModel';
|
import { ControllerModel } from '../../../models/controllerModel';
|
||||||
|
import { MiaaModel } from '../../../models/miaaModel';
|
||||||
import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
||||||
import { ControllerTreeNode } from '../../../ui/tree/controllerTreeNode';
|
import { ControllerTreeNode } from '../../../ui/tree/controllerTreeNode';
|
||||||
|
import { MiaaTreeNode } from '../../../ui/tree/miaaTreeNode';
|
||||||
import { FakeControllerModel } from '../../mocks/fakeControllerModel';
|
import { FakeControllerModel } from '../../mocks/fakeControllerModel';
|
||||||
|
import { FakeAzdataApi } from '../../mocks/fakeAzdataApi';
|
||||||
|
|
||||||
describe('AzureArcTreeDataProvider tests', function (): void {
|
describe('AzureArcTreeDataProvider tests', function (): void {
|
||||||
let treeDataProvider: AzureArcTreeDataProvider;
|
let treeDataProvider: AzureArcTreeDataProvider;
|
||||||
@@ -84,6 +89,27 @@ describe('AzureArcTreeDataProvider tests', function (): void {
|
|||||||
let children = await treeDataProvider.getChildren();
|
let children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(0, 'After loading we should have 0 children');
|
should(children.length).equal(0, 'After loading we should have 0 children');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return all children of controller after loading', async function (): Promise<void> {
|
||||||
|
const mockArcExtension = TypeMoq.Mock.ofType<vscode.Extension<any>>();
|
||||||
|
const mockArcApi = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
||||||
|
mockArcExtension.setup(x => x.exports).returns(() => {
|
||||||
|
return mockArcApi.object;
|
||||||
|
});
|
||||||
|
const fakeAzdataApi = new FakeAzdataApi();
|
||||||
|
fakeAzdataApi.postgresInstances = [{ name: 'pg1', state: '', workers: 0 }];
|
||||||
|
fakeAzdataApi.miaaInstances = [{ name: 'miaa1', state: '', replicas: '', serverEndpoint: '' }];
|
||||||
|
mockArcApi.setup(x => x.azdata).returns(() => fakeAzdataApi);
|
||||||
|
|
||||||
|
sinon.stub(vscode.extensions, 'getExtension').returns(mockArcExtension.object);
|
||||||
|
const controllerModel = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] }, 'mypassword');
|
||||||
|
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.length).equal(2, 'Should have excatly 2 children');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('removeController', function (): void {
|
describe('removeController', function (): void {
|
||||||
@@ -104,4 +130,31 @@ describe('AzureArcTreeDataProvider tests', function (): void {
|
|||||||
should((await treeDataProvider.getChildren()).length).equal(0, 'Removing other node again should do nothing');
|
should((await treeDataProvider.getChildren()).length).equal(0, 'Removing other node again should do nothing');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('openResourceDashboard', function (): void {
|
||||||
|
it('Opening dashboard for nonexistent controller node throws', async function (): Promise<void> {
|
||||||
|
const controllerModel = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
|
||||||
|
const openDashboardPromise = treeDataProvider.openResourceDashboard(controllerModel, ResourceType.sqlManagedInstances, '');
|
||||||
|
await should(openDashboardPromise).be.rejected();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Opening dashboard for nonexistent resource throws', async function (): Promise<void> {
|
||||||
|
const controllerModel = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
|
||||||
|
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
|
const openDashboardPromise = treeDataProvider.openResourceDashboard(controllerModel, ResourceType.sqlManagedInstances, '');
|
||||||
|
await should(openDashboardPromise).be.rejected();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Opening dashboard for existing resource node succeeds', async function (): Promise<void> {
|
||||||
|
const controllerModel = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
|
||||||
|
const miaaModel = new MiaaModel(controllerModel, { name: 'miaa-1', resourceType: ResourceType.sqlManagedInstances }, undefined!, treeDataProvider);
|
||||||
|
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
|
const controllerNode = treeDataProvider.getControllerNode(controllerModel)!;
|
||||||
|
const resourceNode = new MiaaTreeNode(miaaModel, controllerModel);
|
||||||
|
sinon.stub(controllerNode, 'getResourceNode').returns(resourceNode);
|
||||||
|
const showDashboardStub = sinon.stub(resourceNode, 'openDashboard');
|
||||||
|
await treeDataProvider.openResourceDashboard(controllerModel, ResourceType.sqlManagedInstances, '');
|
||||||
|
should(showDashboardStub.calledOnce).be.true('showDashboard should have been called exactly once');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -135,10 +135,14 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
|
|||||||
if (resourceNode) {
|
if (resourceNode) {
|
||||||
await resourceNode.openDashboard();
|
await resourceNode.openDashboard();
|
||||||
} else {
|
} else {
|
||||||
console.log(`Couldn't find resource node for ${name} (${resourceType})`);
|
const errMsg = `Couldn't find resource node for ${name} (${resourceType})`;
|
||||||
|
console.log(errMsg);
|
||||||
|
throw new Error(errMsg);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('Couldn\'t find controller node for opening dashboard');
|
const errMsg = 'Couldn\'t find controller node for opening dashboard';
|
||||||
|
console.log(errMsg);
|
||||||
|
throw new Error(errMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import * as loc from '../../localizedConstants';
|
|||||||
import { ControllerModel, Registration } from '../../models/controllerModel';
|
import { ControllerModel, Registration } from '../../models/controllerModel';
|
||||||
import { MiaaModel } from '../../models/miaaModel';
|
import { MiaaModel } from '../../models/miaaModel';
|
||||||
import { PostgresModel } from '../../models/postgresModel';
|
import { PostgresModel } from '../../models/postgresModel';
|
||||||
|
import { ResourceModel } from '../../models/resourceModel';
|
||||||
import { ControllerDashboard } from '../dashboards/controller/controllerDashboard';
|
import { ControllerDashboard } from '../dashboards/controller/controllerDashboard';
|
||||||
import { AzureArcTreeDataProvider } from './azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from './azureArcTreeDataProvider';
|
||||||
import { MiaaTreeNode } from './miaaTreeNode';
|
import { MiaaTreeNode } from './miaaTreeNode';
|
||||||
@@ -24,7 +25,7 @@ import { TreeNode } from './treeNode';
|
|||||||
*/
|
*/
|
||||||
export class ControllerTreeNode extends TreeNode {
|
export class ControllerTreeNode extends TreeNode {
|
||||||
|
|
||||||
private _children: ResourceTreeNode[] = [];
|
private _children: ResourceTreeNode<ResourceModel>[] = [];
|
||||||
|
|
||||||
constructor(public model: ControllerModel, private _context: vscode.ExtensionContext, private _treeDataProvider: AzureArcTreeDataProvider) {
|
constructor(public model: ControllerModel, private _context: vscode.ExtensionContext, private _treeDataProvider: AzureArcTreeDataProvider) {
|
||||||
super(model.label, vscode.TreeItemCollapsibleState.Collapsed, ResourceType.dataControllers);
|
super(model.label, vscode.TreeItemCollapsibleState.Collapsed, ResourceType.dataControllers);
|
||||||
@@ -69,14 +70,14 @@ export class ControllerTreeNode extends TreeNode {
|
|||||||
* @param resourceType The resourceType of the node
|
* @param resourceType The resourceType of the node
|
||||||
* @param name The name of the node
|
* @param name The name of the node
|
||||||
*/
|
*/
|
||||||
public getResourceNode(resourceType: string, name: string): ResourceTreeNode | undefined {
|
public getResourceNode(resourceType: string, name: string): ResourceTreeNode<ResourceModel> | undefined {
|
||||||
return this._children.find(c =>
|
return this._children.find(c =>
|
||||||
c.model?.info.resourceType === resourceType &&
|
c.model?.info.resourceType === resourceType &&
|
||||||
c.model.info.name === name);
|
c.model.info.name === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateChildren(registrations: Registration[]): void {
|
private updateChildren(registrations: Registration[]): void {
|
||||||
const newChildren: ResourceTreeNode[] = [];
|
const newChildren: ResourceTreeNode<ResourceModel>[] = [];
|
||||||
registrations.forEach(registration => {
|
registrations.forEach(registration => {
|
||||||
if (!registration.instanceName) {
|
if (!registration.instanceName) {
|
||||||
console.warn('Registration is missing required name value, skipping');
|
console.warn('Registration is missing required name value, skipping');
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ import * as vscode from 'vscode';
|
|||||||
import { ControllerModel } from '../../models/controllerModel';
|
import { ControllerModel } from '../../models/controllerModel';
|
||||||
import { MiaaModel } from '../../models/miaaModel';
|
import { MiaaModel } from '../../models/miaaModel';
|
||||||
import { MiaaDashboard } from '../dashboards/miaa/miaaDashboard';
|
import { MiaaDashboard } from '../dashboards/miaa/miaaDashboard';
|
||||||
import { TreeNode } from './treeNode';
|
import { ResourceTreeNode } from './resourceTreeNode';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TreeNode for displaying a SQL Managed Instance on Azure Arc
|
* The TreeNode for displaying a SQL Managed Instance on Azure Arc
|
||||||
*/
|
*/
|
||||||
export class MiaaTreeNode extends TreeNode {
|
export class MiaaTreeNode extends ResourceTreeNode<MiaaModel> {
|
||||||
|
|
||||||
constructor(public model: MiaaModel, private _controllerModel: ControllerModel) {
|
constructor(model: MiaaModel, private _controllerModel: ControllerModel) {
|
||||||
super(model.info.name, vscode.TreeItemCollapsibleState.None, ResourceType.sqlManagedInstances);
|
super(model.info.name, vscode.TreeItemCollapsibleState.None, ResourceType.sqlManagedInstances, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openDashboard(): Promise<void> {
|
public async openDashboard(): Promise<void> {
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ import { ResourceTreeNode } from './resourceTreeNode';
|
|||||||
/**
|
/**
|
||||||
* The TreeNode for displaying an Postgres Server group
|
* The TreeNode for displaying an Postgres Server group
|
||||||
*/
|
*/
|
||||||
export class PostgresTreeNode extends ResourceTreeNode {
|
export class PostgresTreeNode extends ResourceTreeNode<PostgresModel> {
|
||||||
|
|
||||||
constructor(private _model: PostgresModel, private _controllerModel: ControllerModel, private _context: vscode.ExtensionContext) {
|
constructor(model: PostgresModel, private _controllerModel: ControllerModel, private _context: vscode.ExtensionContext) {
|
||||||
super(_model.info.name, vscode.TreeItemCollapsibleState.None, ResourceType.postgresInstances, _model);
|
super(model.info.name, vscode.TreeItemCollapsibleState.None, ResourceType.postgresInstances, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openDashboard(): Promise<void> {
|
public async openDashboard(): Promise<void> {
|
||||||
const postgresDashboard = new PostgresDashboard(this._context, this._controllerModel, this._model);
|
const postgresDashboard = new PostgresDashboard(this._context, this._controllerModel, this.model);
|
||||||
await postgresDashboard.showDashboard();
|
await postgresDashboard.showDashboard();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import { TreeNode } from './treeNode';
|
|||||||
/**
|
/**
|
||||||
* A TreeNode belonging to a child of a Controller
|
* A TreeNode belonging to a child of a Controller
|
||||||
*/
|
*/
|
||||||
export abstract class ResourceTreeNode extends TreeNode {
|
export abstract class ResourceTreeNode<M extends ResourceModel> extends TreeNode {
|
||||||
constructor(label: string, collapsibleState: vscode.TreeItemCollapsibleState, resourceType?: string, public model?: ResourceModel) {
|
constructor(label: string, collapsibleState: vscode.TreeItemCollapsibleState, resourceType: string, public model: M) {
|
||||||
super(label, collapsibleState, resourceType);
|
super(label, collapsibleState, resourceType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user