Skip to content

Commit

Permalink
chore: Adds unit tests for classes
Browse files Browse the repository at this point in the history
* kubernetes-api
 * Mocks @hawtio/react module to export a mock Logger
 * Unit tests for support.ts
 * Unit tests for namespace-client.ts
 * namespace-client-test.ts
  * Mocks CollectionImpl to avoid any network action

* online-shell
 * Mocks @hawtio/react module to export a mock Logger
 * Unit tests for discover-project.ts
 * discover-project-tests.ts
  * Mocks online-oauth, management-api and provides a mock ManagedPod class
  • Loading branch information
phantomjinx committed Oct 1, 2024
1 parent aae104a commit 0a00eee
Show file tree
Hide file tree
Showing 26 changed files with 3,033 additions and 11 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"gen:proxying": "./scripts/generate-proxying.sh",
"gen:serving": "./scripts/generate-serving.sh",
"test": "yarn workspaces foreach -v -Aipt --exclude @hawtio/online-root --exclude \"@hawtio/*-app\" run test",
"test:watch": "yarn workspaces foreach -v -Aipt --exclude @hawtio/online-root --exclude \"@hawtio/*-app\" run test:watch",
"deploy:k8s:namespace": "./scripts/kube-apply.sh deploy/k8s/namespace/",
"deploy:k8s:cluster": "./scripts/kube-apply.sh deploy/k8s/cluster/",
"deploy:openshift:namespace": "./scripts/kube-apply.sh deploy/openshift/namespace/",
Expand Down
3 changes: 2 additions & 1 deletion packages/kubernetes-api/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'path'
export default {
preset: 'ts-jest',
testEnvironment: 'jsdom',
silent: true,
silent: false,

// Automatically clear mock calls and instances between every test
clearMocks: true,
Expand All @@ -18,6 +18,7 @@ export default {
'react-markdown': '<rootDir>/../../node_modules/react-markdown/react-markdown.min.js',
'@patternfly/react-code-editor': path.resolve(__dirname, './src/__mocks__/codeEditorMock.js'),
oauth4webapi: path.resolve(__dirname, './src/__mocks__/oauth4webapi.js'),
'@hawtio/react': path.resolve(__dirname, './src/__mocks__/@hawtioReact.js'),
},

// The path to a module that runs some code to configure or set up the testing
Expand Down
18 changes: 18 additions & 0 deletions packages/kubernetes-api/src/__mocks__/@hawtioReact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Logger {
#name

static get(name) {
return new Logger(name)
}

constructor(name) {
this.#name = name
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
debug(_text) {
/* no-op */
}
}

module.exports = { Logger }
323 changes: 323 additions & 0 deletions packages/kubernetes-api/src/client/namespace-client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
/* eslint-disable import/first */
/*
* Need to disable rule due to using mock class that needs to be undefined
* before import command
*/

import path from 'path'
import fs from 'fs'
import { KubeObject, KubePod } from '../globals'
import { TypeFilter } from '../filter'
import { SortOrder } from '../sort'
import { KOptions, ProcessDataCallback } from './globals'
import { getKey } from './support'

// To be populated each test
let namespacedPods: KubePod[] = []

// To mock CollectionImpl and avoid any network connecting
class MockCollectionImpl {
constructor(private _options: KOptions) {}

get connected(): boolean {
return true
}

getKey() {
return getKey(this._options.kind, this._options.namespace, this._options.name)
}

connect() {
// no-op
}

watch(watchCb: ProcessDataCallback<KubeObject>): ProcessDataCallback<KubeObject> {
if (!this._options.name) {
// No name so this is a namespace client
setTimeout(() => {
watchCb(namespacedPods)
}, 100)
} else {
// Name defined so this is a named pod client
setTimeout(() => {
const namedPods = namespacedPods.filter(pod => pod.metadata?.name === this._options.name)
watchCb(namedPods)
}, 100)
}

return watchCb
}

unwatch(cb: ProcessDataCallback<KubeObject>) {
// no-op
}

destroy() {
// no-op
}
}

jest.mock('./collection', () => {
return {
CollectionImpl: MockCollectionImpl,
}
})

// Import after the MockCollectionImpl class has been defined
import { NamespaceClient } from './namespace-client'

function readPods(limit: number): KubePod[] {
const pods: KubePod[] = []
for (let i = 0; i < limit; ++i) {
const podTestFile = `quarkus-example-pod${i + 1}.json`
const podJsonPath = path.resolve(__dirname, '..', 'testdata', podTestFile)
const podResourceJson = fs.readFileSync(podJsonPath, { encoding: 'utf8', flag: 'r' })
const podResource = JSON.parse(podResourceJson)
pods.push(podResource as KubePod)
}
return pods
}

const NAMESPACE = 'hawtio'

let nc: NamespaceClient

describe('namespace-client', () => {
afterEach(() => {
if (nc) nc.destroy()
})

test('getNamespace', () => {
const cb = (_: KubePod[], __: number) => {
// Nothing to do
}

nc = new NamespaceClient(NAMESPACE, cb)
expect(nc.namespace).toBe(NAMESPACE)
})

test('not-connected-emptyJolokiaPods', () => {
const cb = (_: KubePod[], __: number) => {
// Nothing to do
}

nc = new NamespaceClient(NAMESPACE, cb)
expect(nc.isConnected()).toEqual(false)
expect(nc.getJolokiaPods()).toEqual([])
})

test('connect-default', done => {
const nsLimit = 3

// Read in the test pods
namespacedPods = readPods(2)

// Callback used for the namespace client - use done() to restrict the test
const cb = (jolokiaPods: KubePod[], fullPodCount: number) => {
expect(fullPodCount).toBe(namespacedPods.length)

// Expect jolokiaPods to be limited by the page limit
expect(jolokiaPods.length).toBe(namespacedPods.length > nsLimit ? nsLimit : namespacedPods.length)

expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).not.toBe(-1)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).not.toBe(-1)
done()
}

nc = new NamespaceClient(NAMESPACE, cb)
nc.connect(nsLimit)
expect(nc.isConnected()).toEqual(true)
})

test('connect-page-limited', done => {
const nsLimit = 3

// Read in the test pods
namespacedPods = readPods(4)

// Callback used for the namespace client - use done() to restrict the test
const cb = (jolokiaPods: KubePod[], fullPodCount: number) => {
expect(fullPodCount).toBe(namespacedPods.length)

// Expect jolokiaPods to be limited by the page limit
expect(jolokiaPods.length).toBe(namespacedPods.length > nsLimit ? nsLimit : namespacedPods.length)

// Expect the first 3 pods to be present
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).not.toBe(-1)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).not.toBe(-1)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[2].metadata?.uid)).not.toBe(-1)

// Expect the 4th pod to not be present since it is on the 2nd page
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[3].metadata?.uid)).toBe(-1)

done()
}

nc = new NamespaceClient(NAMESPACE, cb)
nc.connect(nsLimit)
expect(nc.isConnected()).toEqual(true)
})

test('connect-raise-the-page-limit', done => {
const nsLimit = 4

// Read in the test pods
namespacedPods = readPods(4)

// Callback used for the namespace client - use done() to restrict the test
const cb = (jolokiaPods: KubePod[], fullPodCount: number) => {
expect(fullPodCount).toBe(namespacedPods.length)

// Expect jolokiaPods to be limited by the page limit
expect(jolokiaPods.length).toBe(namespacedPods.length > nsLimit ? nsLimit : namespacedPods.length)

// Expect the first 4 pods to all be present
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).not.toBe(-1)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).not.toBe(-1)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[2].metadata?.uid)).not.toBe(-1)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[3].metadata?.uid)).not.toBe(-1)

done()
}

nc = new NamespaceClient(NAMESPACE, cb)
nc.connect(nsLimit)
expect(nc.isConnected()).toEqual(true)
})

test('connect-then-filter-4-3', done => {
const nsLimit = 3

// Read in the test pods
namespacedPods = readPods(4)

// Callback used for the namespace client - use done() to restrict the test
const cb = (jolokiaPods: KubePod[], fullPodCount: number) => {
if (fullPodCount === 4) {
/*
* On return of connect() through the callback initiate the filter call
*/
nc.filter(new TypeFilter([], ['zzzzz']))
return
}

/*
* Since this was a filter we get a callback from notifyChange
* which is necessary if the case where the filter returns the
* same number of pods as the previous collection. However, we
* still need to wait for the 4th previously unwatched pod to
* be added to the collection and so call this callback again.
*/
if (jolokiaPods.length < 3) {
return
}

// filter should only return 3 pods
expect(fullPodCount).toBe(3)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).not.toBe(-1)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[2].metadata?.uid)).not.toBe(-1)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[3].metadata?.uid)).not.toBe(-1)

// Expect the first pod to not be present since it does not conform to the filter
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).toBe(-1)

done()
}

nc = new NamespaceClient(NAMESPACE, cb)
nc.connect(nsLimit)
expect(nc.isConnected()).toEqual(true)
})

test('connect-then-filter-4-1', done => {
const nsLimit = 3

// Read in the test pods
namespacedPods = readPods(4)

// Callback used for the namespace client - use done() to restrict the test
const cb = (jolokiaPods: KubePod[], fullPodCount: number) => {
if (fullPodCount === 4) {
/*
* On return of connect() through the callback initiate the filter call
*/
nc.filter(new TypeFilter([], ['4wdlk']))
return
}

/*
* Since this was a filter we get a callback from notifyChange
* which is necessary if the case where the filter returns the
* same number of pods as the previous collection. However, we
* still need to wait for the 4th previously unwatched pod to
* be added to the collection and so call this callback again.
*/
if (jolokiaPods.length !== 1) {
return
}

// filter should only return 3 pods
expect(fullPodCount).toBe(1)

// Expect the first pod to be present since it only conforms to the filter
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).not.toBe(-1)

done()
}

nc = new NamespaceClient(NAMESPACE, cb)
nc.connect(nsLimit)
expect(nc.isConnected()).toEqual(true)
})

test('connect-then-sort-4', done => {
const nsLimit = 3

// Read in the test pods
namespacedPods = readPods(4)

let firstCallbackComplete = false

// Callback used for the namespace client - use done() to restrict the test
const cb = (jolokiaPods: KubePod[], fullPodCount: number) => {
if (!firstCallbackComplete) {
/*
* On return of connect() through the callback initiate the sort call
*/
firstCallbackComplete = true
nc.sort(SortOrder.DESC)
return
}

/*
* Since this was a sort we get a callback from notifyChange
* which is necessary if the case where the sort returns the
* same number of pods as the previous collection. However, we
* still need to wait for the last previously unwatched pod to
* be added to the collection and so call this callback again.
*/
if (jolokiaPods.length < 3) {
return
}

// paging should only return 3 pods in the right order
expect(jolokiaPods.length).toBe(3)

expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).toBe(2)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[2].metadata?.uid)).toBe(1)
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[3].metadata?.uid)).toBe(0)

/*
* Expect the first pod to not be present since it is now last
* due to the sort and not included in the first page
*/
expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).toBe(-1)

done()
}

nc = new NamespaceClient(NAMESPACE, cb)
nc.connect(nsLimit)
expect(nc.isConnected()).toEqual(true)
})
})
Loading

0 comments on commit 0a00eee

Please sign in to comment.