From 3ec1f7cc2bd11879830fb5f8e25592e547bc622a Mon Sep 17 00:00:00 2001 From: Arvind Ranasaria Date: Tue, 3 Nov 2020 15:46:31 -0800 Subject: [PATCH] Kube Service tests (#13183) --- .../src/services/kubeService.ts | 41 +++++----- .../src/services/notebookService.ts | 1 + .../src/test/services/kubeService.test.ts | 76 +++++++++++++++++++ .../src/test/services/toolsService.test.ts | 18 +++-- 4 files changed, 108 insertions(+), 28 deletions(-) create mode 100644 extensions/resource-deployment/src/test/services/kubeService.test.ts diff --git a/extensions/resource-deployment/src/services/kubeService.ts b/extensions/resource-deployment/src/services/kubeService.ts index 85bf8b57a1..8cc60a2066 100644 --- a/extensions/resource-deployment/src/services/kubeService.ts +++ b/extensions/resource-deployment/src/services/kubeService.ts @@ -28,32 +28,33 @@ export class KubeService implements IKubeService { } } -export function getKubeConfigClusterContexts(configFile: string): Promise { - return fs.promises.access(configFile).catch((error) => { +export async function getKubeConfigClusterContexts(configFile: string): Promise { + try { + await fs.promises.access(configFile); + } catch (error) { if (error && error.code === 'ENOENT') { return []; } else { throw error; } - }).then(() => { - const config = yamljs.load(configFile); - const rawContexts = config['contexts']; - const currentContext = config['current-context']; - const contexts: KubeClusterContext[] = []; - if (currentContext && rawContexts && rawContexts.length > 0) { - rawContexts.forEach(rawContext => { - const name = rawContext['name']; - if (name) { - contexts.push({ - name: name, - isCurrentContext: name === currentContext - }); - } - }); - } - return contexts; - }); + } + const config = yamljs.load(configFile); + const rawContexts = config['contexts']; + const currentContext = config['current-context']; + const contexts: KubeClusterContext[] = []; + if (currentContext && rawContexts && rawContexts.length > 0) { + rawContexts.forEach(rawContext => { + const name = rawContext['name']; + if (name) { + contexts.push({ + name: name, + isCurrentContext: name === currentContext + }); + } + }); + } + return contexts; } export function getDefaultKubeConfigPath(): string { diff --git a/extensions/resource-deployment/src/services/notebookService.ts b/extensions/resource-deployment/src/services/notebookService.ts index 58de02818b..0fe675a8e3 100644 --- a/extensions/resource-deployment/src/services/notebookService.ts +++ b/extensions/resource-deployment/src/services/notebookService.ts @@ -234,3 +234,4 @@ export class NotebookService implements INotebookService { }); } } + diff --git a/extensions/resource-deployment/src/test/services/kubeService.test.ts b/extensions/resource-deployment/src/test/services/kubeService.test.ts new file mode 100644 index 0000000000..d93caefc55 --- /dev/null +++ b/extensions/resource-deployment/src/test/services/kubeService.test.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import 'mocha'; +import * as path from 'path'; +import * as sinon from 'sinon'; +import * as yamljs from 'yamljs'; +import { tryExecuteAction } from '../../common/utils'; +import { getDefaultKubeConfigPath, getKubeConfigClusterContexts, KubeClusterContext, KubeService } from '../../services/kubeService'; + +const kubeConfig = +{ + 'contexts': [ + { + 'context': { + 'cluster': 'docker-desktop', + 'user': 'docker-desktop' + }, + 'name': 'docker-for-desktop' + }, + { + 'context': { + 'cluster': 'kubernetes', + 'user': 'kubernetes-admin' + }, + 'name': 'kubernetes-admin@kubernetes' + } + ], + 'current-context': 'docker-for-desktop' +}; +describe('KubeService', function (): void { + const kubeService: KubeService = new KubeService(); + const configFile = 'kubeConfig'; + + afterEach('NotebookService cleanup', () => { + sinon.restore(); + }); + + it('getDefaultKubeConfigPath', async () => { + getDefaultKubeConfigPath().should.endWith(path.join('.kube', 'config')); + kubeService.getDefaultConfigPath().should.endWith(path.join('.kube', 'config')); + }); + + describe('get Kube Config Cluster Contexts', () => { + it('success', async () => { + sinon.stub(fs.promises, 'access').withArgs(configFile).resolves(); //resolving access to file, mocks its existence + sinon.stub(yamljs, 'load').returns(kubeConfig); + const verifyContexts = (contexts: KubeClusterContext[], testName: string) => { + contexts.length.should.equal(2, `test: ${testName} failed`); + contexts[0].name.should.equal('docker-for-desktop', `test: ${testName} failed`); + contexts[0].isCurrentContext.should.be.true(`test: ${testName} failed`); + contexts[1].name.should.equal('kubernetes-admin@kubernetes', `test: ${testName} failed`); + contexts[1].isCurrentContext.should.be.false(`test: ${testName} failed`); + }; + verifyContexts(await getKubeConfigClusterContexts(configFile), 'getKubeConfigClusterContexts'); + verifyContexts(await kubeService.getClusterContexts(configFile), 'getClusterContexts'); + }); + it('errors with empty array on ENOENT error', async () => { + sinon.stub(fs.promises, 'access').withArgs(configFile).rejects(Object.assign(new Error(), { code: 'ENOENT' })); //rejecting access to file, fakes its non-existence with specific error + const verifyContexts = (contexts: KubeClusterContext[], testName: string) => { + contexts.length.should.equal(0, `test: ${testName} failed`); + }; + verifyContexts(await getKubeConfigClusterContexts(configFile), 'getKubeConfigClusterContexts'); + verifyContexts(await kubeService.getClusterContexts(configFile), 'getClusterContexts'); + }); + it('throws error when unable to access file with non ENOENT error', async () => { + const error = new Error('unknown error accessing file'); + sinon.stub(fs.promises, 'access').withArgs(configFile).rejects(error); //rejecting access to file, fakes its non-existence + ((await tryExecuteAction(() => getKubeConfigClusterContexts(configFile))).error).should.equal(error, `test: getKubeConfigClusterContexts failed`); + ((await tryExecuteAction(() => kubeService.getClusterContexts(configFile))).error).should.equal(error, `test: getClusterContexts failed`); + }); + }); +}); diff --git a/extensions/resource-deployment/src/test/services/toolsService.test.ts b/extensions/resource-deployment/src/test/services/toolsService.test.ts index 41b4f45b3d..f609228bc7 100644 --- a/extensions/resource-deployment/src/test/services/toolsService.test.ts +++ b/extensions/resource-deployment/src/test/services/toolsService.test.ts @@ -3,10 +3,12 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'mocha'; +import assert = require('assert'); import * as TypeMoq from 'typemoq'; +import { ToolsService } from '../../services/toolsService'; import { ITool, ToolType } from '../../interfaces'; import { IPlatformService } from '../../services/platformService'; -import { ToolsService } from '../../services/toolsService'; const tools: { name: string; type: ToolType }[] = [ @@ -30,12 +32,12 @@ describe('Tools Service Tests', function (): void { } } } - (missingTypes.length === 0).should.be.true(`the following enum values are not included in the test:${missingTypes.join(',')}`); + assert(missingTypes.length === 0, `the following enum values are not included in the test:${missingTypes.join(',')}`); tools.forEach(toolInfo => { const tool = toolsService.getToolByName(toolInfo.name); - (!!tool).should.be.true(`The tool: ${toolInfo.name} is not recognized`); - (tool!.type).should.equal(toolInfo.type, 'returned notebook name does not match expected value'); + assert(!!tool, `The tool: ${toolInfo.name} is not recognized`); + assert.equal(tool!.type, toolInfo.type, 'returned notebook name does not match expected value'); }); }); @@ -43,17 +45,17 @@ describe('Tools Service Tests', function (): void { const mockPlatformService = TypeMoq.Mock.ofType(); const toolsService = new ToolsService(mockPlatformService.object); const tool = toolsService.getToolByName('no-such-tool'); - (tool === undefined).should.be.true('for a not defined tool, expected value is undefined'); + assert(tool === undefined, 'for a not defined tool, expected value is undefined'); }); it('get/set tools for CurrentProvider', () => { const iTools: ITool[] = tools.map(toolInfo => { const tool = toolsService.getToolByName(toolInfo.name); - (!!tool).should.be.true(`The tool: ${toolInfo.name} is not recognized`); - tool!.type.should.equal(toolInfo.type, 'returned notebook name does not match expected value'); + assert(!!tool, `The tool: ${toolInfo.name} is not recognized`); + assert.equal(tool!.type, toolInfo.type, 'returned notebook name does not match expected value'); return tool!; }); toolsService.toolsForCurrentProvider = iTools; - iTools.should.deepEqual(toolsService.toolsForCurrentProvider, 'toolsForCurrentProvider did not return the value we set'); + assert.deepEqual(iTools, toolsService.toolsForCurrentProvider, 'toolsForCurrentProvider did not return the value we set'); }); });