codify-plugin-test 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +10 -3
- package/dist/plugin-tester.d.ts +12 -3
- package/dist/plugin-tester.js +60 -26
- package/dist/plugin-tester.js.map +1 -1
- package/package.json +2 -2
- package/src/plugin-tester.ts +89 -30
- package/test/plugin-tester.test.ts +1 -1
package/.eslintrc.json
CHANGED
|
@@ -9,9 +9,16 @@
|
|
|
9
9
|
"warn",
|
|
10
10
|
"always"
|
|
11
11
|
],
|
|
12
|
-
"perfectionist/sort-classes":
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
"perfectionist/sort-classes": "off",
|
|
13
|
+
"perfectionist/sort-interfaces": "off",
|
|
14
|
+
"perfectionist/sort-enums": "off",
|
|
15
|
+
"perfectionist/sort-objects": "off",
|
|
16
|
+
"perfectionist/sort-object-types": "off",
|
|
17
|
+
"unicorn/no-array-reduce": "off",
|
|
18
|
+
"unicorn/no-array-for-each": "off",
|
|
19
|
+
"unicorn/prefer-object-from-entries": "off",
|
|
20
|
+
"unicorn/prefer-type-error": "off",
|
|
21
|
+
"no-await-in-loop": "off",
|
|
15
22
|
"quotes": [
|
|
16
23
|
"error",
|
|
17
24
|
"single"
|
package/dist/plugin-tester.d.ts
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import { ApplyRequestData, InitializeResponseData, PlanRequestData, PlanResponseData, ResourceConfig, ValidateRequestData, ValidateResponseData } from 'codify-schemas';
|
|
1
|
+
import { ApplyRequestData, ImportRequestData, ImportResponseData, InitializeResponseData, PlanRequestData, PlanResponseData, ResourceConfig, ValidateRequestData, ValidateResponseData } from 'codify-schemas';
|
|
2
2
|
import { ChildProcess } from 'node:child_process';
|
|
3
3
|
export declare class PluginTester {
|
|
4
4
|
childProcess: ChildProcess;
|
|
5
5
|
constructor(pluginPath: string);
|
|
6
|
-
fullTest(configs: ResourceConfig[],
|
|
7
|
-
|
|
6
|
+
fullTest(configs: ResourceConfig[], options?: {
|
|
7
|
+
skipUninstall?: boolean;
|
|
8
|
+
validatePlan?: (plans: PlanResponseData[]) => Promise<void> | void;
|
|
9
|
+
validateApply?: (plans: PlanResponseData[]) => Promise<void> | void;
|
|
10
|
+
validateDestroy?: (plans: PlanResponseData[]) => Promise<void> | void;
|
|
11
|
+
validateImport?: (importResults: (ImportResponseData['result'][0])[]) => Promise<void> | void;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
uninstall(configs: ResourceConfig[], options?: {
|
|
14
|
+
validateDestroy?: (plans: PlanResponseData[]) => Promise<void> | void;
|
|
15
|
+
}): Promise<void>;
|
|
8
16
|
initialize(): Promise<InitializeResponseData>;
|
|
9
17
|
validate(data: ValidateRequestData): Promise<ValidateResponseData>;
|
|
10
18
|
plan(data: PlanRequestData): Promise<PlanResponseData>;
|
|
11
19
|
apply(data: ApplyRequestData): Promise<void>;
|
|
20
|
+
import(data: ImportRequestData): Promise<ImportResponseData>;
|
|
12
21
|
kill(): void;
|
|
13
22
|
private handleSudoRequests;
|
|
14
23
|
}
|
package/dist/plugin-tester.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import Ajv from 'ajv';
|
|
2
|
-
import { IpcMessageSchema, MessageCmd,
|
|
2
|
+
import { IpcMessageSchema, MessageCmd, ResourceOperation, SpawnStatus, SudoRequestDataSchema } from 'codify-schemas';
|
|
3
3
|
import { fork, spawn } from 'node:child_process';
|
|
4
|
+
import inspector from 'node:inspector';
|
|
4
5
|
import path from 'node:path';
|
|
5
6
|
import { CodifyTestUtils } from './test-utils.js';
|
|
6
|
-
import inspector from 'node:inspector';
|
|
7
7
|
const ajv = new Ajv.default({
|
|
8
8
|
strict: true
|
|
9
9
|
});
|
|
@@ -15,14 +15,17 @@ export class PluginTester {
|
|
|
15
15
|
if (!path.isAbsolute(pluginPath)) {
|
|
16
16
|
throw new Error('A fully qualified path must be supplied to PluginTester');
|
|
17
17
|
}
|
|
18
|
+
console.log('Node Inspector:');
|
|
19
|
+
console.log(inspector.url());
|
|
18
20
|
this.childProcess = fork(pluginPath, [], {
|
|
19
21
|
detached: true,
|
|
20
22
|
env: { ...process.env },
|
|
21
|
-
execArgv: ['--import', 'tsx/esm',
|
|
23
|
+
execArgv: ['--import', 'tsx/esm', '--inspect=9221'],
|
|
22
24
|
});
|
|
23
25
|
this.handleSudoRequests(this.childProcess);
|
|
24
26
|
}
|
|
25
|
-
async fullTest(configs,
|
|
27
|
+
async fullTest(configs, options) {
|
|
28
|
+
const { skipUninstall = false, } = options ?? {};
|
|
26
29
|
const initializeResult = await this.initialize();
|
|
27
30
|
const unsupportedConfigs = configs.filter((c) => !initializeResult.resourceDefinitions.some((rd) => rd.type === c.type));
|
|
28
31
|
if (unsupportedConfigs.length > 0) {
|
|
@@ -41,8 +44,8 @@ export class PluginTester {
|
|
|
41
44
|
state: undefined,
|
|
42
45
|
}));
|
|
43
46
|
}
|
|
44
|
-
if (
|
|
45
|
-
|
|
47
|
+
if (options?.validatePlan) {
|
|
48
|
+
await options.validatePlan(plans);
|
|
46
49
|
}
|
|
47
50
|
for (const plan of plans) {
|
|
48
51
|
await this.apply({
|
|
@@ -62,35 +65,63 @@ export class PluginTester {
|
|
|
62
65
|
throw new Error(`The following applies were not successful. Re-running plan shows that the resources did not return no-op but instead returned:
|
|
63
66
|
${JSON.stringify(unsuccessfulPlans, null, 2)}`);
|
|
64
67
|
}
|
|
68
|
+
if (options?.validateApply) {
|
|
69
|
+
await options.validateApply(plans);
|
|
70
|
+
}
|
|
71
|
+
const importResults = [];
|
|
72
|
+
const unsuccessfulImports = [];
|
|
73
|
+
for (const config of configs) {
|
|
74
|
+
const importResult = await this.import({ config });
|
|
75
|
+
importResults.push(importResult);
|
|
76
|
+
if (importResult.result.length !== 1 ||
|
|
77
|
+
Object.entries(config).some(([k, v]) => importResult.result[0][k] !== v)) {
|
|
78
|
+
unsuccessfulImports.push(importResult);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (unsuccessfulImports.length > 0) {
|
|
82
|
+
throw new Error(`The following imports were not successful. The imports differed from the original.
|
|
83
|
+
${JSON.stringify(unsuccessfulImports, null, 2)}`);
|
|
84
|
+
}
|
|
85
|
+
if (options?.validateImport) {
|
|
86
|
+
await options.validateImport(importResults.map((r) => r.result[0]));
|
|
87
|
+
}
|
|
88
|
+
if (!skipUninstall) {
|
|
89
|
+
await this.uninstall(configs.toReversed(), options);
|
|
90
|
+
}
|
|
65
91
|
}
|
|
66
|
-
async uninstall(configs) {
|
|
92
|
+
async uninstall(configs, options) {
|
|
93
|
+
const plans = [];
|
|
67
94
|
for (const config of configs) {
|
|
68
|
-
|
|
95
|
+
plans.push(await this.plan({
|
|
96
|
+
desired: undefined,
|
|
97
|
+
isStateful: true,
|
|
98
|
+
state: config
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
for (const plan of plans) {
|
|
102
|
+
if (plan.operation !== ResourceOperation.DESTROY) {
|
|
103
|
+
throw new Error(`Expect resource operation to be 'destory' but instead received plan: \n ${JSON.stringify(plan, null, 2)}`);
|
|
104
|
+
}
|
|
69
105
|
await this.apply({
|
|
70
|
-
|
|
71
|
-
operation: ResourceOperation.DESTROY,
|
|
72
|
-
parameters: Object.entries(parameters).map(([key, value]) => ({
|
|
73
|
-
name: key,
|
|
74
|
-
newValue: null,
|
|
75
|
-
operation: ParameterOperation.REMOVE,
|
|
76
|
-
previousValue: value,
|
|
77
|
-
})),
|
|
78
|
-
resourceType: type,
|
|
79
|
-
}
|
|
106
|
+
planId: plan.planId
|
|
80
107
|
});
|
|
108
|
+
}
|
|
109
|
+
for (const config of configs) {
|
|
81
110
|
const validationPlan = await this.plan({
|
|
82
111
|
desired: config,
|
|
83
|
-
|
|
84
|
-
|
|
112
|
+
isStateful: true,
|
|
113
|
+
state: undefined
|
|
85
114
|
});
|
|
86
115
|
if (validationPlan.operation !== ResourceOperation.CREATE) {
|
|
87
|
-
throw new Error(`Resource
|
|
116
|
+
throw new Error(`Resource was not successfully destroyed.
|
|
88
117
|
Validation plan shows:
|
|
89
118
|
${JSON.stringify(validationPlan, null, 2)}
|
|
90
|
-
|
|
91
|
-
${JSON.stringify(config, null, 2)}`);
|
|
119
|
+
`);
|
|
92
120
|
}
|
|
93
121
|
}
|
|
122
|
+
if (options?.validateDestroy) {
|
|
123
|
+
await options.validateDestroy(plans);
|
|
124
|
+
}
|
|
94
125
|
}
|
|
95
126
|
async initialize() {
|
|
96
127
|
return CodifyTestUtils.sendMessageAndAwaitResponse(this.childProcess, {
|
|
@@ -116,6 +147,12 @@ ${JSON.stringify(config, null, 2)}`);
|
|
|
116
147
|
data,
|
|
117
148
|
});
|
|
118
149
|
}
|
|
150
|
+
async import(data) {
|
|
151
|
+
return CodifyTestUtils.sendMessageAndAwaitResponse(this.childProcess, {
|
|
152
|
+
cmd: 'import',
|
|
153
|
+
data,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
119
156
|
kill() {
|
|
120
157
|
this.childProcess.kill();
|
|
121
158
|
}
|
|
@@ -168,7 +205,4 @@ async function sudoSpawn(cmd, opts) {
|
|
|
168
205
|
});
|
|
169
206
|
});
|
|
170
207
|
}
|
|
171
|
-
function isInDebugMode() {
|
|
172
|
-
return inspector.url() !== undefined;
|
|
173
|
-
}
|
|
174
208
|
//# sourceMappingURL=plugin-tester.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-tester.js","sourceRoot":"","sources":["../src/plugin-tester.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,
|
|
1
|
+
{"version":3,"file":"plugin-tester.js","sourceRoot":"","sources":["../src/plugin-tester.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codify-plugin-test",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"ajv": "^8.12.0",
|
|
17
17
|
"ajv-formats": "^3.0.1",
|
|
18
|
-
"codify-schemas": "1.0.
|
|
18
|
+
"codify-schemas": "1.0.52"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@oclif/prettier-config": "^0.2.1",
|
package/src/plugin-tester.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import Ajv from 'ajv';
|
|
2
2
|
import {
|
|
3
3
|
ApplyRequestData,
|
|
4
|
+
ImportRequestData,
|
|
5
|
+
ImportResponseData,
|
|
4
6
|
InitializeResponseData,
|
|
5
7
|
IpcMessageSchema,
|
|
6
8
|
MessageCmd,
|
|
7
|
-
ParameterOperation,
|
|
8
9
|
PlanRequestData,
|
|
9
10
|
PlanResponseData,
|
|
10
11
|
ResourceConfig,
|
|
@@ -16,10 +17,10 @@ import {
|
|
|
16
17
|
ValidateResponseData
|
|
17
18
|
} from 'codify-schemas';
|
|
18
19
|
import { ChildProcess, SpawnOptions, fork, spawn } from 'node:child_process';
|
|
20
|
+
import inspector from 'node:inspector'
|
|
19
21
|
import path from 'node:path';
|
|
20
22
|
|
|
21
23
|
import { CodifyTestUtils } from './test-utils.js';
|
|
22
|
-
import inspector from 'node:inspector';
|
|
23
24
|
|
|
24
25
|
const ajv = new Ajv.default({
|
|
25
26
|
strict: true
|
|
@@ -27,6 +28,7 @@ const ajv = new Ajv.default({
|
|
|
27
28
|
const ipcMessageValidator = ajv.compile(IpcMessageSchema);
|
|
28
29
|
const sudoRequestValidator = ajv.compile(SudoRequestDataSchema);
|
|
29
30
|
|
|
31
|
+
|
|
30
32
|
export class PluginTester {
|
|
31
33
|
childProcess: ChildProcess
|
|
32
34
|
|
|
@@ -40,6 +42,9 @@ export class PluginTester {
|
|
|
40
42
|
throw new Error('A fully qualified path must be supplied to PluginTester');
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
console.log('Node Inspector:')
|
|
46
|
+
console.log(inspector.url());
|
|
47
|
+
|
|
43
48
|
this.childProcess = fork(
|
|
44
49
|
pluginPath,
|
|
45
50
|
[],
|
|
@@ -47,14 +52,26 @@ export class PluginTester {
|
|
|
47
52
|
// Use default true to test plugins in secure mode (un-able to request sudo directly)
|
|
48
53
|
detached: true,
|
|
49
54
|
env: { ...process.env },
|
|
50
|
-
execArgv: ['--import', 'tsx/esm',
|
|
55
|
+
execArgv: ['--import', 'tsx/esm', '--inspect=9221'],
|
|
51
56
|
},
|
|
52
57
|
)
|
|
53
58
|
|
|
54
59
|
this.handleSudoRequests(this.childProcess);
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
async fullTest(
|
|
62
|
+
async fullTest(
|
|
63
|
+
configs: ResourceConfig[],
|
|
64
|
+
options?: {
|
|
65
|
+
skipUninstall?: boolean,
|
|
66
|
+
validatePlan?: (plans: PlanResponseData[]) => Promise<void> | void
|
|
67
|
+
validateApply?: (plans: PlanResponseData[]) => Promise<void> | void,
|
|
68
|
+
validateDestroy?: (plans: PlanResponseData[]) => Promise<void> | void,
|
|
69
|
+
validateImport?: (importResults: (ImportResponseData['result'][0])[]) => Promise<void> | void,
|
|
70
|
+
}): Promise<void> {
|
|
71
|
+
const {
|
|
72
|
+
skipUninstall = false,
|
|
73
|
+
} = options ?? {}
|
|
74
|
+
|
|
58
75
|
const initializeResult = await this.initialize();
|
|
59
76
|
|
|
60
77
|
const unsupportedConfigs = configs.filter((c) =>
|
|
@@ -80,8 +97,8 @@ export class PluginTester {
|
|
|
80
97
|
}));
|
|
81
98
|
}
|
|
82
99
|
|
|
83
|
-
if (
|
|
84
|
-
|
|
100
|
+
if (options?.validatePlan) {
|
|
101
|
+
await options.validatePlan(plans);
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
for (const plan of plans) {
|
|
@@ -106,40 +123,79 @@ export class PluginTester {
|
|
|
106
123
|
${JSON.stringify(unsuccessfulPlans, null, 2)}`
|
|
107
124
|
)
|
|
108
125
|
}
|
|
126
|
+
|
|
127
|
+
if (options?.validateApply) {
|
|
128
|
+
await options.validateApply(plans);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const importResults = [];
|
|
132
|
+
const unsuccessfulImports = [];
|
|
133
|
+
for (const config of configs) {
|
|
134
|
+
const importResult = await this.import({ config })
|
|
135
|
+
importResults.push(importResult);
|
|
136
|
+
|
|
137
|
+
if (importResult.result.length !== 1 ||
|
|
138
|
+
Object.entries(config).some(([k, v]) => importResult.result[0][k] !== v)
|
|
139
|
+
) {
|
|
140
|
+
unsuccessfulImports.push(importResult);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (unsuccessfulImports.length > 0) {
|
|
145
|
+
throw new Error(`The following imports were not successful. The imports differed from the original.
|
|
146
|
+
${JSON.stringify(unsuccessfulImports, null, 2)}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (options?.validateImport) {
|
|
150
|
+
await options.validateImport(importResults.map((r) => r.result[0]));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!skipUninstall) {
|
|
154
|
+
await this.uninstall(configs.toReversed(), options);
|
|
155
|
+
}
|
|
109
156
|
}
|
|
110
157
|
|
|
111
|
-
async uninstall(configs: ResourceConfig[]
|
|
158
|
+
async uninstall(configs: ResourceConfig[], options?: {
|
|
159
|
+
validateDestroy?: (plans: PlanResponseData[]) => Promise<void> | void
|
|
160
|
+
}) {
|
|
161
|
+
const plans = [];
|
|
162
|
+
|
|
112
163
|
for (const config of configs) {
|
|
113
|
-
|
|
164
|
+
plans.push(await this.plan({
|
|
165
|
+
desired: undefined,
|
|
166
|
+
isStateful: true,
|
|
167
|
+
state: config
|
|
168
|
+
}))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
for (const plan of plans) {
|
|
172
|
+
if (plan.operation !== ResourceOperation.DESTROY) {
|
|
173
|
+
throw new Error(`Expect resource operation to be 'destory' but instead received plan: \n ${JSON.stringify(plan, null, 2)}`)
|
|
174
|
+
}
|
|
114
175
|
|
|
115
176
|
await this.apply({
|
|
116
|
-
|
|
117
|
-
operation: ResourceOperation.DESTROY,
|
|
118
|
-
parameters: Object.entries(parameters).map(([key, value]) => ({
|
|
119
|
-
name: key,
|
|
120
|
-
newValue: null,
|
|
121
|
-
operation: ParameterOperation.REMOVE,
|
|
122
|
-
previousValue: value,
|
|
123
|
-
})),
|
|
124
|
-
resourceType: type,
|
|
125
|
-
}
|
|
177
|
+
planId: plan.planId
|
|
126
178
|
});
|
|
179
|
+
}
|
|
127
180
|
|
|
128
|
-
|
|
181
|
+
// Validate that the destroy was successful
|
|
182
|
+
for (const config of configs) {
|
|
129
183
|
const validationPlan = await this.plan({
|
|
130
184
|
desired: config,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
})
|
|
185
|
+
isStateful: true,
|
|
186
|
+
state: undefined
|
|
187
|
+
})
|
|
134
188
|
if (validationPlan.operation !== ResourceOperation.CREATE) {
|
|
135
|
-
throw new Error(`Resource
|
|
189
|
+
throw new Error(`Resource was not successfully destroyed.
|
|
136
190
|
Validation plan shows:
|
|
137
191
|
${JSON.stringify(validationPlan, null, 2)}
|
|
138
|
-
|
|
139
|
-
${JSON.stringify(config, null, 2)}`
|
|
140
|
-
);
|
|
192
|
+
`);
|
|
141
193
|
}
|
|
142
194
|
}
|
|
195
|
+
|
|
196
|
+
if (options?.validateDestroy) {
|
|
197
|
+
await options.validateDestroy(plans);
|
|
198
|
+
}
|
|
143
199
|
}
|
|
144
200
|
|
|
145
201
|
async initialize(): Promise<InitializeResponseData> {
|
|
@@ -170,6 +226,13 @@ ${JSON.stringify(config, null, 2)}`
|
|
|
170
226
|
});
|
|
171
227
|
}
|
|
172
228
|
|
|
229
|
+
async import(data: ImportRequestData): Promise<ImportResponseData> {
|
|
230
|
+
return CodifyTestUtils.sendMessageAndAwaitResponse(this.childProcess, {
|
|
231
|
+
cmd: 'import',
|
|
232
|
+
data,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
173
236
|
kill() {
|
|
174
237
|
this.childProcess.kill();
|
|
175
238
|
}
|
|
@@ -257,7 +320,3 @@ async function sudoSpawn(
|
|
|
257
320
|
})
|
|
258
321
|
})
|
|
259
322
|
}
|
|
260
|
-
|
|
261
|
-
function isInDebugMode() {
|
|
262
|
-
return inspector.url() !== undefined;
|
|
263
|
-
}
|
|
@@ -139,7 +139,7 @@ describe('Plugin tester integration tests', () => {
|
|
|
139
139
|
propA: 'a',
|
|
140
140
|
propB: 10,
|
|
141
141
|
propC: 'c',
|
|
142
|
-
}], (plans) => {
|
|
142
|
+
}], false, (plans) => {
|
|
143
143
|
expect(plans[0]).toMatchObject({
|
|
144
144
|
planId: expect.any(String),
|
|
145
145
|
operation: ResourceOperation.NOOP,
|