Skip to content

Commit e3fc9c8

Browse files
committed
Refactor and added pre-requisite parameter to fullTest
1 parent 4894262 commit e3fc9c8

1 file changed

Lines changed: 191 additions & 150 deletions

File tree

src/plugin-tester.ts

Lines changed: 191 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -10,84 +10,54 @@ import unionBy from 'lodash.unionby';
1010
import { PluginProcess } from './plugin-process.js';
1111
import { getPlatformOs, splitUserConfig } from './utils.js';
1212

13+
export interface FullTestOptions {
14+
skipUninstall?: boolean,
15+
skipImport?: boolean,
16+
17+
// Ensure these resources are installed before testing starts. Will install if missing, but never destroy after.
18+
prerequisites?: ResourceConfig[],
19+
20+
validatePlan?: (plans: PlanResponseData[]) => Promise<void> | void
21+
validateApply?: (plans: PlanResponseData[]) => Promise<void> | void,
22+
validateDestroy?: (plans: PlanResponseData[]) => Promise<void> | void,
23+
validateImport?: (importResults: (ImportResponseData['result'][0])[]) => Promise<void> | void,
24+
testModify?: {
25+
modifiedConfigs: ResourceConfig[],
26+
validateModify?: (plans: PlanResponseData[]) => Promise<void> | void,
27+
}
28+
}
29+
1330
export class PluginTester {
1431
static async fullTest(
1532
pluginPath: string,
1633
configs: ResourceConfig[],
17-
options?: {
18-
skipUninstall?: boolean,
19-
skipImport?: boolean,
20-
validatePlan?: (plans: PlanResponseData[]) => Promise<void> | void
21-
validateApply?: (plans: PlanResponseData[]) => Promise<void> | void,
22-
validateDestroy?: (plans: PlanResponseData[]) => Promise<void> | void,
23-
validateImport?: (importResults: (ImportResponseData['result'][0])[]) => Promise<void> | void,
24-
testModify?: {
25-
modifiedConfigs: ResourceConfig[],
26-
validateModify?: (plans: PlanResponseData[]) => Promise<void> | void,
27-
}
28-
}): Promise<void> {
34+
options?: FullTestOptions,
35+
): Promise<void> {
2936
configs = configs.filter((c) => !c.os || c.os.includes(getPlatformOs()));
3037
const ids = configs
3138
.map((c) => `${c.type}${c.name ? `.${c.name}` : ''}`)
3239
.join(', ')
3340
console.info(chalk.cyan(`Starting full test of [ ${ids} ]...`));
3441

42+
const { skipUninstall = false } = options ?? {}
3543

36-
const {
37-
skipUninstall = false,
38-
} = options ?? {}
39-
44+
if (options?.prerequisites?.length) {
45+
await this.ensurePrerequisites(pluginPath, options.prerequisites);
46+
}
4047

4148
const plugin = new PluginProcess(pluginPath);
4249
try {
43-
console.info(chalk.cyan('Testing initialization...'))
44-
const initializeResult = await plugin.initialize();
45-
46-
const unsupportedConfigs = configs.filter((c) =>
47-
!initializeResult.resourceDefinitions.some((rd) => rd.type === c.type)
48-
)
49-
if (unsupportedConfigs.length > 0) {
50-
throw new Error(`The plugin does not support the following configs supplied:\n ${JSON.stringify(unsupportedConfigs, null, 2)}\n Initialize result: ${JSON.stringify(initializeResult)}`)
51-
}
52-
53-
// configs = configs.filter((c) => initializeResult.resourceDefinitions.find((rd) => rd.type === c.type)?.operatingSystems?.includes(os.platform() as OS));
54-
55-
console.info(chalk.cyan('Testing validate...'))
56-
const validate = await plugin.validate({
57-
configs: configs.map((c) => {
58-
const { coreParameters, parameters } = splitUserConfig(c)
59-
return { core: coreParameters, parameters };
60-
})
61-
});
62-
63-
const invalidConfigs = validate.resourceValidations.filter((v) => !v.isValid)
64-
if (invalidConfigs.length > 0) {
65-
throw new Error(`The following configs did not validate:\n ${JSON.stringify(invalidConfigs, null, 2)}`)
66-
}
50+
await this.initializeAndValidate(plugin, configs);
6751

6852
console.info(chalk.cyan('Testing plan...'))
69-
const plans = [];
70-
for (const config of configs) {
71-
const { coreParameters, parameters } = splitUserConfig(config);
72-
73-
plans.push(await plugin.plan({
74-
core: coreParameters,
75-
desired: parameters,
76-
isStateful: false,
77-
state: undefined,
78-
}));
79-
}
53+
const plans = await this.planConfigs(plugin, configs);
8054

8155
if (options?.validatePlan) {
8256
await options.validatePlan(plans);
8357
}
8458

8559
console.info(chalk.cyan('Testing apply...'))
86-
for (const plan of plans) {
87-
await plugin.apply({
88-
planId: plan.planId
89-
});
90-
}
60+
await this.applyPlans(plugin, plans);
9161

9262
if (options?.validateApply) {
9363
await options.validateApply(plans);
@@ -97,63 +67,11 @@ export class PluginTester {
9767
}
9868

9969
if (!options?.skipImport) {
100-
const importPlugin = new PluginProcess(pluginPath);
101-
try {
102-
await importPlugin.initialize();
103-
console.info(chalk.cyan('Testing import...'))
104-
105-
const importResults = [];
106-
for (const config of configs) {
107-
const { coreParameters, parameters } = splitUserConfig(config);
108-
109-
const importResult = await importPlugin.import({ core: coreParameters, parameters })
110-
importResults.push(importResult);
111-
}
112-
113-
if (options?.validateImport) {
114-
await options.validateImport(importResults.map((r) => r.result[0]));
115-
}
116-
} finally {
117-
importPlugin.kill();
118-
}
70+
await this.runImportPhase(pluginPath, configs, options?.validateImport);
11971
}
12072

12173
if (options?.testModify) {
122-
const modifyPlugin = new PluginProcess(pluginPath);
123-
124-
try {
125-
await modifyPlugin.initialize();
126-
console.info(chalk.cyan('Testing modify...'))
127-
128-
const modifyPlans = [];
129-
for (const config of options.testModify.modifiedConfigs) {
130-
const { coreParameters, parameters } = splitUserConfig(config);
131-
132-
modifyPlans.push(await modifyPlugin.plan({
133-
core: coreParameters,
134-
desired: parameters,
135-
isStateful: false,
136-
state: undefined,
137-
}));
138-
}
139-
140-
if (modifyPlans.some((p) => p.operation !== ResourceOperation.MODIFY)) {
141-
throw new Error(`Error while testing modify. Non-modify results were found in the plan:
142-
${JSON.stringify(modifyPlans, null, 2)}`)
143-
}
144-
145-
for (const plan of modifyPlans) {
146-
await modifyPlugin.apply({
147-
planId: plan.planId
148-
});
149-
}
150-
151-
if (options.testModify.validateModify) {
152-
await options.testModify.validateModify(modifyPlans);
153-
}
154-
} finally {
155-
modifyPlugin.kill();
156-
}
74+
await this.runModifyPhase(pluginPath, options.testModify);
15775
}
15876

15977
if (!skipUninstall) {
@@ -172,50 +90,13 @@ ${JSON.stringify(modifyPlans, null, 2)}`)
17290
const plugin = new PluginProcess(pluginPath);
17391

17492
try {
175-
console.info(chalk.cyan('Testing initialization...'))
176-
const initializeResult = await plugin.initialize();
177-
178-
const unsupportedConfigs = configs.filter((c) =>
179-
!initializeResult.resourceDefinitions.some((rd) => rd.type === c.type)
180-
)
181-
if (unsupportedConfigs.length > 0) {
182-
throw new Error(`The plugin does not support the following configs supplied:\n ${JSON.stringify(unsupportedConfigs, null, 2)}\n Initialize result: ${JSON.stringify(initializeResult)}`)
183-
}
184-
185-
// configs = configs.filter((c) => initializeResult.resourceDefinitions.find((rd) => rd.type === c.type)?.operatingSystems?.includes(os.platform() as OS));
186-
187-
console.info(chalk.cyan('Testing validate...'))
188-
const validate = await plugin.validate({
189-
configs: configs.map((c) => {
190-
const { coreParameters, parameters } = splitUserConfig(c)
191-
return { core: coreParameters, parameters };
192-
})
193-
});
194-
195-
const invalidConfigs = validate.resourceValidations.filter((v) => !v.isValid)
196-
if (invalidConfigs.length > 0) {
197-
throw new Error(`The following configs did not validate:\n ${JSON.stringify(invalidConfigs, null, 2)}`)
198-
}
93+
await this.initializeAndValidate(plugin, configs);
19994

20095
console.info(chalk.cyan('Testing plan...'))
201-
const plans = [];
202-
for (const config of configs) {
203-
const { coreParameters, parameters } = splitUserConfig(config);
204-
205-
plans.push(await plugin.plan({
206-
core: coreParameters,
207-
desired: parameters,
208-
isStateful: false,
209-
state: undefined,
210-
}));
211-
}
96+
const plans = await this.planConfigs(plugin, configs);
21297

21398
console.info(chalk.cyan('Testing apply...'))
214-
for (const plan of plans) {
215-
await plugin.apply({
216-
planId: plan.planId
217-
});
218-
}
99+
await this.applyPlans(plugin, plans);
219100
} finally {
220101
plugin.kill();
221102
}
@@ -260,6 +141,166 @@ ${JSON.stringify(modifyPlans, null, 2)}`)
260141
}
261142
}
262143

144+
private static async ensurePrerequisites(pluginPath: string, prerequisites: ResourceConfig[]): Promise<void> {
145+
prerequisites = prerequisites.filter((c) => !c.os || c.os.includes(getPlatformOs()));
146+
if (prerequisites.length === 0) return;
147+
148+
const ids = prerequisites.map((c) => `${c.type}${c.name ? `.${c.name}` : ''}`).join(', ');
149+
console.info(chalk.cyan(`Checking prerequisites [ ${ids} ]...`));
150+
151+
const plugin = new PluginProcess(pluginPath);
152+
try {
153+
const initializeResult = await plugin.initialize();
154+
155+
const unsupportedConfigs = prerequisites.filter((c) =>
156+
!initializeResult.resourceDefinitions.some((rd) => rd.type === c.type)
157+
)
158+
if (unsupportedConfigs.length > 0) {
159+
throw new Error(`The plugin does not support the following prerequisite configs:\n ${JSON.stringify(unsupportedConfigs, null, 2)}`)
160+
}
161+
162+
const validate = await plugin.validate({
163+
configs: prerequisites.map((c) => {
164+
const { coreParameters, parameters } = splitUserConfig(c)
165+
return { core: coreParameters, parameters };
166+
})
167+
});
168+
169+
const invalidConfigs = validate.resourceValidations.filter((v) => !v.isValid)
170+
if (invalidConfigs.length > 0) {
171+
console.error(chalk.red(`Prerequisites validation failed:\n ${JSON.stringify(invalidConfigs, null, 2)}`));
172+
throw new Error(`The following prerequisite configs did not validate:\n ${JSON.stringify(invalidConfigs, null, 2)}`)
173+
}
174+
175+
for (const config of prerequisites) {
176+
const { coreParameters, parameters } = splitUserConfig(config);
177+
const plan = await plugin.plan({
178+
core: coreParameters,
179+
desired: parameters,
180+
isStateful: false,
181+
state: undefined,
182+
});
183+
184+
const label = `${config.type}${config.name ? `.${config.name}` : ''}`;
185+
if (plan.operation === ResourceOperation.NOOP) {
186+
console.info(chalk.cyan(`Prerequisite already satisfied: ${label}`));
187+
} else {
188+
await plugin.apply({ planId: plan.planId });
189+
console.info(chalk.cyan(`Prerequisite installed: ${label}`));
190+
}
191+
}
192+
} finally {
193+
plugin.kill();
194+
}
195+
}
196+
197+
private static async initializeAndValidate(plugin: PluginProcess, configs: ResourceConfig[]): Promise<void> {
198+
console.info(chalk.cyan('Testing initialization...'))
199+
const initializeResult = await plugin.initialize();
200+
201+
const unsupportedConfigs = configs.filter((c) =>
202+
!initializeResult.resourceDefinitions.some((rd) => rd.type === c.type)
203+
)
204+
if (unsupportedConfigs.length > 0) {
205+
throw new Error(`The plugin does not support the following configs supplied:\n ${JSON.stringify(unsupportedConfigs, null, 2)}\n Initialize result: ${JSON.stringify(initializeResult)}`)
206+
}
207+
208+
console.info(chalk.cyan('Testing validate...'))
209+
const validate = await plugin.validate({
210+
configs: configs.map((c) => {
211+
const { coreParameters, parameters } = splitUserConfig(c)
212+
return { core: coreParameters, parameters };
213+
})
214+
});
215+
216+
const invalidConfigs = validate.resourceValidations.filter((v) => !v.isValid)
217+
if (invalidConfigs.length > 0) {
218+
throw new Error(`The following configs did not validate:\n ${JSON.stringify(invalidConfigs, null, 2)}`)
219+
}
220+
}
221+
222+
private static async planConfigs(plugin: PluginProcess, configs: ResourceConfig[]): Promise<PlanResponseData[]> {
223+
const plans = [];
224+
for (const config of configs) {
225+
const { coreParameters, parameters } = splitUserConfig(config);
226+
plans.push(await plugin.plan({
227+
core: coreParameters,
228+
desired: parameters,
229+
isStateful: false,
230+
state: undefined,
231+
}));
232+
}
233+
234+
return plans;
235+
}
236+
237+
private static async applyPlans(plugin: PluginProcess, plans: PlanResponseData[]): Promise<void> {
238+
for (const plan of plans) {
239+
await plugin.apply({ planId: plan.planId });
240+
}
241+
}
242+
243+
private static async runImportPhase(
244+
pluginPath: string,
245+
configs: ResourceConfig[],
246+
validateImport?: (importResults: (ImportResponseData['result'][0])[]) => Promise<void> | void,
247+
): Promise<void> {
248+
const importPlugin = new PluginProcess(pluginPath);
249+
try {
250+
await importPlugin.initialize();
251+
console.info(chalk.cyan('Testing import...'))
252+
253+
const importResults = [];
254+
for (const config of configs) {
255+
const { coreParameters, parameters } = splitUserConfig(config);
256+
importResults.push(await importPlugin.import({ core: coreParameters, parameters }));
257+
}
258+
259+
if (validateImport) {
260+
await validateImport(importResults.map((r) => r.result[0]));
261+
}
262+
} finally {
263+
importPlugin.kill();
264+
}
265+
}
266+
267+
private static async runModifyPhase(
268+
pluginPath: string,
269+
testModify: { modifiedConfigs: ResourceConfig[], validateModify?: (plans: PlanResponseData[]) => Promise<void> | void },
270+
): Promise<void> {
271+
const modifyPlugin = new PluginProcess(pluginPath);
272+
273+
try {
274+
await modifyPlugin.initialize();
275+
console.info(chalk.cyan('Testing modify...'))
276+
277+
const modifyPlans = [];
278+
for (const config of testModify.modifiedConfigs) {
279+
const { coreParameters, parameters } = splitUserConfig(config);
280+
281+
modifyPlans.push(await modifyPlugin.plan({
282+
core: coreParameters,
283+
desired: parameters,
284+
isStateful: false,
285+
state: undefined,
286+
}));
287+
}
288+
289+
if (modifyPlans.some((p) => p.operation !== ResourceOperation.MODIFY)) {
290+
throw new Error(`Error while testing modify. Non-modify results were found in the plan:
291+
${JSON.stringify(modifyPlans, null, 2)}`)
292+
}
293+
294+
await this.applyPlans(modifyPlugin, modifyPlans);
295+
296+
if (testModify.validateModify) {
297+
await testModify.validateModify(modifyPlans);
298+
}
299+
} finally {
300+
modifyPlugin.kill();
301+
}
302+
}
303+
263304
private static addNamesToConfigs(configs: ResourceConfig[]): ResourceConfig[] {
264305
const configsWithNames = new Array<ResourceConfig>();
265306

0 commit comments

Comments
 (0)