-
Notifications
You must be signed in to change notification settings - Fork 593
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kubevirt: add tests for Wrappers, EnhancedK8sMethods and utility classes
- fix bugs
- Loading branch information
Showing
19 changed files
with
803 additions
and
48 deletions.
There are no files selected for viewing
205 changes: 205 additions & 0 deletions
205
.../packages/kubevirt-plugin/src/k8s/enhancedK8sMethods/__tests__/enhancedK8sMethods.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
import { EnhancedK8sMethods } from '../enhancedK8sMethods'; | ||
import { HistoryItem, HistoryType } from '../types'; | ||
|
||
import { VirtualMachineModel } from '../../../models'; | ||
import { VMWrapper } from '../../wrapper/vm/vm-wrapper'; | ||
import { PatchBuilder } from '@console/shared/src/k8s'; | ||
import { K8sKind, K8sResourceCommon, Patch } from '@console/internal/module/k8s'; | ||
import { K8sKillError } from '../errors'; | ||
|
||
const disableHistoryOpts = { disableHistory: true }; | ||
|
||
const spyEnhancedK8sMethods = () => { | ||
const methods = new EnhancedK8sMethods(); | ||
// mocks the approximate behaviour of CRUD methods | ||
const methodsUnchecked = methods as any; | ||
methodsUnchecked.k8sCreate = async ( | ||
kind: K8sKind, | ||
data: K8sResourceCommon, | ||
opts, | ||
enhancedOpts, | ||
) => { | ||
methodsUnchecked.registerKind(kind); | ||
methodsUnchecked.appendHistory(new HistoryItem(HistoryType.CREATE, data), enhancedOpts); | ||
}; | ||
// different behaviour than the original - we need to store the testVM | ||
methodsUnchecked.k8sGet = async (kind: K8sKind, data: K8sResourceCommon, opts, enhancedOpts) => { | ||
methodsUnchecked.registerKind(kind); | ||
methodsUnchecked.appendHistory(new HistoryItem(HistoryType.GET, data), enhancedOpts); | ||
}; | ||
methodsUnchecked.k8sPatch = async ( | ||
kind: K8sKind, | ||
data: K8sResourceCommon, | ||
patches: Patch[], | ||
enhancedOpts, | ||
) => { | ||
methodsUnchecked.registerKind(kind); | ||
methodsUnchecked.appendHistory( | ||
new HistoryItem(HistoryType.PATCH, { ...data, ...patches[0] }), | ||
enhancedOpts, | ||
); | ||
}; | ||
methodsUnchecked.k8sKill = async ( | ||
kind: K8sKind, | ||
data: K8sResourceCommon, | ||
opts, | ||
json, | ||
enhancedOpts, | ||
) => { | ||
methodsUnchecked.registerKind(kind); | ||
methodsUnchecked.appendHistory(new HistoryItem(HistoryType.DELETE, data), enhancedOpts); | ||
}; | ||
return { methods, methodsUnchecked }; | ||
}; | ||
|
||
const expectHistory = (history, expectedHistory) => { | ||
expect(history).toHaveLength(expectedHistory.length); | ||
|
||
expectedHistory.forEach((expectHistoryItem, idx) => { | ||
expect(history[idx]).toEqual(expectHistoryItem); | ||
}); | ||
}; | ||
|
||
describe('enhancedK8sMethods.js', () => { | ||
const testVM = new VMWrapper() | ||
.setModel(VirtualMachineModel) | ||
.setName('testVM') | ||
.setNamespace('default') | ||
.setMemory('5', 'Gi') | ||
.asResource(); | ||
|
||
const otherTestVM = new VMWrapper() | ||
.setModel(VirtualMachineModel) | ||
.setName('otherVM') | ||
.setNamespace('kube') | ||
.setMemory('1', 'Mi') | ||
.asResource(); | ||
|
||
const testPatch = new PatchBuilder('/') | ||
.add({ | ||
additionalData: 'testData', | ||
}) | ||
.build(); | ||
|
||
it('records history and shows actualState', async () => { | ||
const { methods, methodsUnchecked } = spyEnhancedK8sMethods(); | ||
|
||
expect(methods.getHistory()).toHaveLength(0); | ||
expect(methods.getActualState()).toHaveLength(0); | ||
|
||
await methods.k8sCreate(VirtualMachineModel, testVM); | ||
await methods.k8sPatch(VirtualMachineModel, testVM, [testPatch]); | ||
await methods.k8sKill(VirtualMachineModel, testVM); | ||
const history = methods.getHistory(); | ||
|
||
expectHistory(history, [ | ||
new HistoryItem(HistoryType.CREATE, testVM), | ||
new HistoryItem(HistoryType.PATCH, { ...testVM, ...testPatch }), | ||
new HistoryItem(HistoryType.DELETE, testVM), | ||
]); | ||
|
||
expect(methodsUnchecked.history).toHaveLength(3); | ||
}); | ||
|
||
it('shows actualState', async () => { | ||
const { methods, methodsUnchecked } = spyEnhancedK8sMethods(); | ||
|
||
await methods.k8sCreate(VirtualMachineModel, testVM); | ||
await methods.k8sPatch(VirtualMachineModel, testVM, []); | ||
await methods.k8sPatch(VirtualMachineModel, testVM, []); | ||
await methodsUnchecked.k8sGet(VirtualMachineModel, testVM); | ||
await methods.k8sKill(VirtualMachineModel, testVM); | ||
await methods.k8sCreate(VirtualMachineModel, otherTestVM); | ||
await methods.k8sPatch(VirtualMachineModel, otherTestVM, []); | ||
await methodsUnchecked.k8sGet(VirtualMachineModel, otherTestVM); | ||
await methods.k8sCreate(VirtualMachineModel, testVM); | ||
await methodsUnchecked.k8sGet(VirtualMachineModel, testVM); | ||
await methods.k8sPatch(VirtualMachineModel, testVM, [testPatch]); | ||
const actualState = methods.getActualState(); | ||
|
||
expect(actualState).toHaveLength(2); | ||
expect(actualState[0]).toEqual(otherTestVM); | ||
expect(actualState[1]).toEqual({ ...testVM, ...testPatch }); | ||
}); | ||
|
||
it('disables recording history', async () => { | ||
const { methods, methodsUnchecked } = spyEnhancedK8sMethods(); | ||
|
||
await methods.k8sCreate(VirtualMachineModel, testVM, null, disableHistoryOpts); | ||
await methods.k8sPatch(VirtualMachineModel, testVM, [testPatch], disableHistoryOpts); | ||
await methods.k8sKill(VirtualMachineModel, testVM, null, null, disableHistoryOpts); | ||
|
||
expect(methods.getHistory()).toHaveLength(0); | ||
expect(methodsUnchecked.history).toHaveLength(0); | ||
|
||
await methods.k8sCreate(VirtualMachineModel, testVM); | ||
expect(methods.getHistory()).toHaveLength(1); | ||
}); | ||
|
||
it('rollback', async () => { | ||
const { methods } = spyEnhancedK8sMethods(); | ||
|
||
await methods.k8sCreate(VirtualMachineModel, testVM); | ||
await methods.k8sPatch(VirtualMachineModel, testVM, []); | ||
await methods.k8sCreate(VirtualMachineModel, otherTestVM); | ||
|
||
const results = await methods.rollback(); | ||
|
||
expect(results).toHaveLength(2); | ||
expect(methods.getActualState()).toHaveLength(0); | ||
|
||
expectHistory(methods.getHistory(), [ | ||
new HistoryItem(HistoryType.CREATE, testVM), | ||
new HistoryItem(HistoryType.PATCH, testVM), | ||
new HistoryItem(HistoryType.CREATE, otherTestVM), | ||
// reverse order of deletion | ||
new HistoryItem(HistoryType.DELETE, otherTestVM), | ||
new HistoryItem(HistoryType.DELETE, testVM), | ||
]); | ||
}); | ||
|
||
it('rollback fails', async () => { | ||
const { methods, methodsUnchecked } = spyEnhancedK8sMethods(); | ||
methodsUnchecked.k8sKill = async () => Promise.reject(new Error('delete failed')); | ||
|
||
await methods.k8sCreate(VirtualMachineModel, testVM); | ||
await methods.k8sPatch(VirtualMachineModel, testVM, []); | ||
await methods.k8sCreate(VirtualMachineModel, otherTestVM); | ||
|
||
expect.assertions(4); | ||
let rollbackError; | ||
try { | ||
await methods.rollback(); | ||
} catch (e) { | ||
rollbackError = e; | ||
} | ||
expect(rollbackError?.message).toEqual('rollback'); | ||
expect(rollbackError?.errors).toHaveLength(2); | ||
|
||
// does not change EnhancedK8sMethods and results | ||
expect(methods.getActualState()).toHaveLength(2); | ||
expect(methods.getHistory()).toHaveLength(3); | ||
}); | ||
|
||
it('rollback does not fail on 404', async () => { | ||
const { methods, methodsUnchecked } = spyEnhancedK8sMethods(); | ||
methodsUnchecked.k8sKill = async (kind: K8sKind, data: K8sResourceCommon) => | ||
Promise.reject(new K8sKillError('delete failed', { code: 404 }, data)); | ||
|
||
await methods.k8sCreate(VirtualMachineModel, testVM); | ||
await methods.k8sPatch(VirtualMachineModel, testVM, []); | ||
await methods.k8sCreate(VirtualMachineModel, otherTestVM); | ||
|
||
const results = await methods.rollback(); | ||
expect(results).toHaveLength(2); | ||
expect(methods.getActualState()).toHaveLength(0); | ||
|
||
expectHistory(methods.getHistory(), [ | ||
new HistoryItem(HistoryType.CREATE, testVM), | ||
new HistoryItem(HistoryType.PATCH, testVM), | ||
new HistoryItem(HistoryType.CREATE, otherTestVM), | ||
new HistoryItem(HistoryType.NOT_FOUND, otherTestVM), | ||
new HistoryItem(HistoryType.NOT_FOUND, testVM), | ||
]); | ||
}); | ||
}); |
85 changes: 85 additions & 0 deletions
85
frontend/packages/kubevirt-plugin/src/k8s/wrapper/common/__tests__/mocks/smoothie-wrapper.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { ObjectEnum } from '../../../../../constants'; | ||
import { ObjectWithTypePropertyWrapper } from '../../object-with-type-property-wrapper'; | ||
|
||
export type Smoothie = { | ||
attributes: { | ||
price?: { | ||
value: number; | ||
currency: string; | ||
}; | ||
exclusiveFlavor: { | ||
banana?: { | ||
color?: string; | ||
peel?: { | ||
thickness: string; | ||
}; | ||
}; | ||
orange?: { | ||
color?: string; | ||
seeds?: { | ||
count: number; | ||
}; | ||
}; | ||
blueberry?: { | ||
color?: string; | ||
}; | ||
strawberry?: { | ||
color?: string; | ||
}; | ||
}; | ||
}; | ||
}; | ||
|
||
export type CombinedExclusiveFlavorTypeData = { | ||
color?: string; | ||
peel?: { | ||
thickness: string; | ||
}; | ||
seeds?: { | ||
count: number; | ||
}; | ||
}; | ||
|
||
export class SmoothieType extends ObjectEnum<string> { | ||
static readonly STRAWBERRY = new SmoothieType('strawberry'); | ||
|
||
static readonly BANANA = new SmoothieType('banana'); | ||
|
||
static readonly ORANGE = new SmoothieType('orange'); | ||
|
||
static readonly BLUEBERRY = new SmoothieType('blueberry'); | ||
|
||
private static readonly ALL = Object.freeze( | ||
ObjectEnum.getAllClassEnumProperties<SmoothieType>(SmoothieType), | ||
); | ||
|
||
static getAll = () => SmoothieType.ALL; | ||
} | ||
|
||
export class SmoothieWrapper extends ObjectWithTypePropertyWrapper< | ||
Smoothie, | ||
SmoothieType, | ||
CombinedExclusiveFlavorTypeData, | ||
SmoothieWrapper | ||
> { | ||
constructor(data?: Smoothie | SmoothieWrapper, copy = false) { | ||
super(data, copy, SmoothieType, ['attributes', 'exclusiveFlavor']); | ||
} | ||
|
||
protected sanitize( | ||
type: SmoothieType, | ||
typeData: CombinedExclusiveFlavorTypeData, | ||
): CombinedExclusiveFlavorTypeData { | ||
const result: CombinedExclusiveFlavorTypeData = {}; | ||
if (typeData.color) { | ||
result.color = typeData.color; | ||
} | ||
if (type === SmoothieType.BANANA && typeData.peel) { | ||
result.peel = { thickness: typeData.peel.thickness }; | ||
} | ||
if (type === SmoothieType.ORANGE && typeData.seeds) { | ||
result.seeds = { count: typeData.seeds.count }; | ||
} | ||
return result; | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
frontend/packages/kubevirt-plugin/src/k8s/wrapper/common/__tests__/mocks/test-wrapper.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Wrapper } from '../../wrapper'; | ||
|
||
export type TestData = { | ||
color?: string; | ||
interval?: { | ||
from?: number; | ||
to?: number; | ||
}; | ||
location?: string; | ||
}; | ||
|
||
export class TestWrapper extends Wrapper<TestData, TestWrapper> {} |
Oops, something went wrong.