codify-plugin-test 0.0.53-beta2 → 0.0.53-beta20
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/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/plugin-process.d.ts +5 -0
- package/dist/plugin-process.js +23 -21
- package/dist/plugin-process.js.map +1 -1
- package/dist/plugin-tester.d.ts +1 -0
- package/dist/plugin-tester.js +59 -7
- package/dist/plugin-tester.js.map +1 -1
- package/dist/spawn.d.ts +1 -0
- package/dist/spawn.js +11 -2
- package/dist/spawn.js.map +1 -1
- package/dist/test-utils.d.ts +39 -1
- package/dist/test-utils.js +98 -2
- package/dist/test-utils.js.map +1 -1
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +22 -1
- package/dist/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/plugin-process.ts +15 -20
- package/src/plugin-tester.ts +79 -15
- package/src/spawn.ts +6 -2
- package/src/test-utils.test.ts +5 -5
- package/src/test-utils.ts +116 -2
- package/src/utils.ts +25 -2
- package/test/plugin-tester.test.ts +19 -8
- package/test/test-plugin.ts +34 -5
- package/tsconfig.json +1 -2
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,UAAU,EAAuB,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,UAAU,eAAe,CAC7B,MAA0B;IAE1B,MAAM,cAAc,GAAG;QACrB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxC,CAAC;IAEF,6DAA6D;IAC7D,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,UAAU,EAAE,GAAG,MAAM,CAAC;IAE5D,OAAO;QACL,UAAU,EAAE,UAAe;QAC3B,cAAc;KACf,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAC7B,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,OAAO,UAAU,CAAC,KAAK,CAAC;QAC1B,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,OAAO,UAAU,CAAC,KAAK,CAAC;QAC1B,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,OAAO,UAAU,CAAC,OAAO,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codify-plugin-test",
|
|
3
|
-
"version": "0.0.53-
|
|
3
|
+
"version": "0.0.53-beta20",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"ajv": "^8.12.0",
|
|
18
18
|
"ajv-formats": "^3.0.1",
|
|
19
|
-
"codify-schemas": "1.0.86-
|
|
19
|
+
"codify-schemas": "1.0.86-beta7",
|
|
20
20
|
"lodash.matches": "^4.6.0",
|
|
21
21
|
"lodash.differencewith": "4.5.0",
|
|
22
22
|
"lodash.unionby": "^4.8.0",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"eslint-config-oclif-typescript": "^3",
|
|
38
38
|
"eslint-config-prettier": "^9.0.0",
|
|
39
39
|
"tsx": "^4.7.3",
|
|
40
|
-
"typescript": "
|
|
40
|
+
"typescript": "5.3.3",
|
|
41
41
|
"vitest": "^1.4.0",
|
|
42
42
|
"codify-plugin-lib": "1.0.182-beta9"
|
|
43
43
|
},
|
package/src/index.ts
CHANGED
package/src/plugin-process.ts
CHANGED
|
@@ -21,7 +21,7 @@ import * as os from 'node:os';
|
|
|
21
21
|
import path from 'node:path';
|
|
22
22
|
|
|
23
23
|
import { spawnSafe } from './spawn.js';
|
|
24
|
-
import {
|
|
24
|
+
import { TestUtils } from './test-utils.js';
|
|
25
25
|
|
|
26
26
|
const ajv = new Ajv.default({
|
|
27
27
|
strict: true
|
|
@@ -64,7 +64,7 @@ export class PluginProcess {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async initialize(): Promise<InitializeResponseData> {
|
|
67
|
-
return
|
|
67
|
+
return TestUtils.sendMessageAndAwaitResponse(this.childProcess, {
|
|
68
68
|
cmd: 'initialize',
|
|
69
69
|
data: { verbosityLevel: 3 },
|
|
70
70
|
requestId: nanoid(6),
|
|
@@ -72,7 +72,7 @@ export class PluginProcess {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
async validate(data: ValidateRequestData): Promise<ValidateResponseData> {
|
|
75
|
-
return
|
|
75
|
+
return TestUtils.sendMessageAndAwaitResponse(this.childProcess, {
|
|
76
76
|
cmd: 'validate',
|
|
77
77
|
data,
|
|
78
78
|
requestId: nanoid(6),
|
|
@@ -80,7 +80,7 @@ export class PluginProcess {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
async plan(data: PlanRequestData): Promise<PlanResponseData> {
|
|
83
|
-
return
|
|
83
|
+
return TestUtils.sendMessageAndAwaitResponse(this.childProcess, {
|
|
84
84
|
cmd: 'plan',
|
|
85
85
|
data,
|
|
86
86
|
requestId: nanoid(6),
|
|
@@ -88,7 +88,7 @@ export class PluginProcess {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
async apply(data: ApplyRequestData): Promise<void> {
|
|
91
|
-
return
|
|
91
|
+
return TestUtils.sendMessageAndAwaitResponse(this.childProcess, {
|
|
92
92
|
cmd: 'apply',
|
|
93
93
|
data,
|
|
94
94
|
requestId: nanoid(6),
|
|
@@ -96,7 +96,7 @@ export class PluginProcess {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
async import(data: ImportRequestData): Promise<ImportResponseData> {
|
|
99
|
-
return
|
|
99
|
+
return TestUtils.sendMessageAndAwaitResponse(this.childProcess, {
|
|
100
100
|
cmd: 'import',
|
|
101
101
|
data,
|
|
102
102
|
requestId: nanoid(6),
|
|
@@ -107,9 +107,9 @@ export class PluginProcess {
|
|
|
107
107
|
this.childProcess.kill();
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
private handleIncomingRequests(
|
|
110
|
+
private handleIncomingRequests(cp: ChildProcess) {
|
|
111
111
|
// Listen for incoming sudo incoming sudo requests
|
|
112
|
-
|
|
112
|
+
cp.on('message', async (message) => {
|
|
113
113
|
if (!ipcMessageValidator(message)) {
|
|
114
114
|
throw new Error(`Invalid message from plugin. ${JSON.stringify(message, null, 2)}`);
|
|
115
115
|
}
|
|
@@ -123,7 +123,7 @@ export class PluginProcess {
|
|
|
123
123
|
const { command, options } = data as unknown as CommandRequestData;
|
|
124
124
|
const result = await spawnSafe(command, options);
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
cp.send(<IpcMessageV2>{
|
|
127
127
|
cmd: MessageCmd.COMMAND_REQUEST + '_Response',
|
|
128
128
|
data: result,
|
|
129
129
|
requestId,
|
|
@@ -136,7 +136,7 @@ export class PluginProcess {
|
|
|
136
136
|
throw new Error(`Invalid sudo request from plugin. ${JSON.stringify(commandRequestValidator.errors, null, 2)}`);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
cp.send(<IpcMessageV2>{
|
|
140
140
|
cmd: MessageCmd.PRESS_KEY_TO_CONTINUE_REQUEST + '_Response',
|
|
141
141
|
data: {},
|
|
142
142
|
requestId,
|
|
@@ -146,19 +146,14 @@ export class PluginProcess {
|
|
|
146
146
|
if (message.cmd === MessageCmd.CODIFY_CREDENTIALS_REQUEST) {
|
|
147
147
|
const { requestId } = message;
|
|
148
148
|
|
|
149
|
-
const
|
|
150
|
-
if (!
|
|
151
|
-
throw new Error('Unable to
|
|
149
|
+
const testJwt = process.env.VITE_CODIFY_TEST_JWT;
|
|
150
|
+
if (!testJwt) {
|
|
151
|
+
throw new Error('Unable to parse login credentials from VITE_CODIFY_TEST_JWT env var. Please set and try again');
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
if (!login) {
|
|
156
|
-
throw new Error('Unable to parse login credentials')
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
process.send(<IpcMessageV2>{
|
|
154
|
+
cp.send(<IpcMessageV2>{
|
|
160
155
|
cmd: MessageCmd.CODIFY_CREDENTIALS_REQUEST + '_Response',
|
|
161
|
-
data:
|
|
156
|
+
data: testJwt,
|
|
162
157
|
requestId,
|
|
163
158
|
})
|
|
164
159
|
}
|
package/src/plugin-tester.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import {
|
|
3
|
-
ImportResponseData,
|
|
3
|
+
ImportResponseData, OS,
|
|
4
4
|
PlanResponseData,
|
|
5
5
|
ResourceConfig,
|
|
6
6
|
ResourceOperation,
|
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
import unionBy from 'lodash.unionby';
|
|
9
9
|
|
|
10
10
|
import { PluginProcess } from './plugin-process.js';
|
|
11
|
-
import { splitUserConfig } from './utils.js';
|
|
11
|
+
import { getPlatformOs, splitUserConfig } from './utils.js';
|
|
12
|
+
import os from 'node:os';
|
|
12
13
|
|
|
13
14
|
export class PluginTester {
|
|
14
15
|
static async fullTest(
|
|
@@ -25,9 +26,13 @@ export class PluginTester {
|
|
|
25
26
|
modifiedConfigs: ResourceConfig[],
|
|
26
27
|
validateModify?: (plans: PlanResponseData[]) => Promise<void> | void,
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
}): Promise<void> {
|
|
30
|
+
configs = configs.filter((c) => !c.os || c.os.includes(getPlatformOs()));
|
|
31
|
+
const ids = configs
|
|
32
|
+
.map((c) => `${c.type}${c.name ? `.${c.name}` : ''}`)
|
|
33
|
+
.join(', ')
|
|
34
|
+
console.info(chalk.cyan(`Starting full test of [ ${ids} ]...`));
|
|
35
|
+
|
|
31
36
|
|
|
32
37
|
const {
|
|
33
38
|
skipUninstall = false,
|
|
@@ -46,11 +51,15 @@ export class PluginTester {
|
|
|
46
51
|
throw new Error(`The plugin does not support the following configs supplied:\n ${JSON.stringify(unsupportedConfigs, null, 2)}\n Initialize result: ${JSON.stringify(initializeResult)}`)
|
|
47
52
|
}
|
|
48
53
|
|
|
54
|
+
// configs = configs.filter((c) => initializeResult.resourceDefinitions.find((rd) => rd.type === c.type)?.operatingSystems?.includes(os.platform() as OS));
|
|
55
|
+
|
|
49
56
|
console.info(chalk.cyan('Testing validate...'))
|
|
50
|
-
const validate = await plugin.validate({
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
const validate = await plugin.validate({
|
|
58
|
+
configs: configs.map((c) => {
|
|
59
|
+
const { coreParameters, parameters } = splitUserConfig(c)
|
|
60
|
+
return { core: coreParameters, parameters };
|
|
61
|
+
})
|
|
62
|
+
});
|
|
54
63
|
|
|
55
64
|
const invalidConfigs = validate.resourceValidations.filter((v) => !v.isValid)
|
|
56
65
|
if (invalidConfigs.length > 0) {
|
|
@@ -61,7 +70,7 @@ export class PluginTester {
|
|
|
61
70
|
const plans = [];
|
|
62
71
|
for (const config of configs) {
|
|
63
72
|
const { coreParameters, parameters } = splitUserConfig(config);
|
|
64
|
-
|
|
73
|
+
|
|
65
74
|
plans.push(await plugin.plan({
|
|
66
75
|
core: coreParameters,
|
|
67
76
|
desired: parameters,
|
|
@@ -91,7 +100,7 @@ export class PluginTester {
|
|
|
91
100
|
if (!options?.skipImport) {
|
|
92
101
|
const importPlugin = new PluginProcess(pluginPath);
|
|
93
102
|
try {
|
|
94
|
-
await
|
|
103
|
+
await importPlugin.initialize();
|
|
95
104
|
console.info(chalk.cyan('Testing import...'))
|
|
96
105
|
|
|
97
106
|
const importResults = [];
|
|
@@ -114,13 +123,13 @@ export class PluginTester {
|
|
|
114
123
|
const modifyPlugin = new PluginProcess(pluginPath);
|
|
115
124
|
|
|
116
125
|
try {
|
|
117
|
-
await
|
|
126
|
+
await modifyPlugin.initialize();
|
|
118
127
|
console.info(chalk.cyan('Testing modify...'))
|
|
119
128
|
|
|
120
129
|
const modifyPlans = [];
|
|
121
130
|
for (const config of options.testModify.modifiedConfigs) {
|
|
122
131
|
const { coreParameters, parameters } = splitUserConfig(config);
|
|
123
|
-
|
|
132
|
+
|
|
124
133
|
modifyPlans.push(await modifyPlugin.plan({
|
|
125
134
|
core: coreParameters,
|
|
126
135
|
desired: parameters,
|
|
@@ -160,6 +169,59 @@ ${JSON.stringify(modifyPlans, null, 2)}`)
|
|
|
160
169
|
}
|
|
161
170
|
}
|
|
162
171
|
|
|
172
|
+
static async install(pluginPath: string, configs: ResourceConfig[]) {
|
|
173
|
+
const plugin = new PluginProcess(pluginPath);
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
console.info(chalk.cyan('Testing initialization...'))
|
|
177
|
+
const initializeResult = await plugin.initialize();
|
|
178
|
+
|
|
179
|
+
const unsupportedConfigs = configs.filter((c) =>
|
|
180
|
+
!initializeResult.resourceDefinitions.some((rd) => rd.type === c.type)
|
|
181
|
+
)
|
|
182
|
+
if (unsupportedConfigs.length > 0) {
|
|
183
|
+
throw new Error(`The plugin does not support the following configs supplied:\n ${JSON.stringify(unsupportedConfigs, null, 2)}\n Initialize result: ${JSON.stringify(initializeResult)}`)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// configs = configs.filter((c) => initializeResult.resourceDefinitions.find((rd) => rd.type === c.type)?.operatingSystems?.includes(os.platform() as OS));
|
|
187
|
+
|
|
188
|
+
console.info(chalk.cyan('Testing validate...'))
|
|
189
|
+
const validate = await plugin.validate({
|
|
190
|
+
configs: configs.map((c) => {
|
|
191
|
+
const { coreParameters, parameters } = splitUserConfig(c)
|
|
192
|
+
return { core: coreParameters, parameters };
|
|
193
|
+
})
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const invalidConfigs = validate.resourceValidations.filter((v) => !v.isValid)
|
|
197
|
+
if (invalidConfigs.length > 0) {
|
|
198
|
+
throw new Error(`The following configs did not validate:\n ${JSON.stringify(invalidConfigs, null, 2)}`)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.info(chalk.cyan('Testing plan...'))
|
|
202
|
+
const plans = [];
|
|
203
|
+
for (const config of configs) {
|
|
204
|
+
const { coreParameters, parameters } = splitUserConfig(config);
|
|
205
|
+
|
|
206
|
+
plans.push(await plugin.plan({
|
|
207
|
+
core: coreParameters,
|
|
208
|
+
desired: parameters,
|
|
209
|
+
isStateful: false,
|
|
210
|
+
state: undefined,
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.info(chalk.cyan('Testing apply...'))
|
|
215
|
+
for (const plan of plans) {
|
|
216
|
+
await plugin.apply({
|
|
217
|
+
planId: plan.planId
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
} finally {
|
|
221
|
+
plugin.kill();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
163
225
|
static async uninstall(pluginPath: string, configs: ResourceConfig[], options?: {
|
|
164
226
|
validateDestroy?: (plans: PlanResponseData[]) => Promise<void> | void
|
|
165
227
|
}) {
|
|
@@ -172,7 +234,7 @@ ${JSON.stringify(modifyPlans, null, 2)}`)
|
|
|
172
234
|
const plans = [];
|
|
173
235
|
for (const config of configs) {
|
|
174
236
|
const { coreParameters, parameters } = splitUserConfig(config);
|
|
175
|
-
|
|
237
|
+
|
|
176
238
|
plans.push(await destroyPlugin.plan({
|
|
177
239
|
core: coreParameters,
|
|
178
240
|
isStateful: true,
|
|
@@ -206,7 +268,9 @@ ${JSON.stringify(modifyPlans, null, 2)}`)
|
|
|
206
268
|
for (const type of typeSet) {
|
|
207
269
|
const sameTypeConfigs = configs.filter((c) => c.type === type);
|
|
208
270
|
if (sameTypeConfigs.length > 1) {
|
|
209
|
-
sameTypeConfigs.forEach((c, idx) => {
|
|
271
|
+
sameTypeConfigs.forEach((c, idx) => {
|
|
272
|
+
c.name = c.name ?? idx.toString()
|
|
273
|
+
});
|
|
210
274
|
}
|
|
211
275
|
|
|
212
276
|
configsWithNames.push(...sameTypeConfigs);
|
package/src/spawn.ts
CHANGED
|
@@ -18,12 +18,16 @@ export interface SpawnOptions {
|
|
|
18
18
|
stdin?: boolean,
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export function testSpawn(cmd: string, options?: SpawnOptions): Promise<SpawnResult> {
|
|
22
|
+
return spawnSafe(cmd, { interactive: true, ...options, });
|
|
23
|
+
}
|
|
24
|
+
|
|
21
25
|
export function spawnSafe(cmd: string, options?: SpawnOptions): Promise<SpawnResult> {
|
|
22
26
|
if (cmd.toLowerCase().includes('sudo')) {
|
|
23
27
|
throw new Error('Command must not include sudo')
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
console.log(`Running command: ${options?.requiresRoot ? 'sudo' : ''} ${cmd}` + (options?.cwd ? `(${options?.cwd})` : ''))
|
|
27
31
|
|
|
28
32
|
return new Promise((resolve) => {
|
|
29
33
|
const output: string[] = [];
|
|
@@ -40,7 +44,7 @@ export function spawnSafe(cmd: string, options?: SpawnOptions): Promise<SpawnRes
|
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
// Initial terminal dimensions
|
|
43
|
-
const initialCols = process.stdout.columns ??
|
|
47
|
+
const initialCols = process.stdout.columns ?? 2000;
|
|
44
48
|
const initialRows = process.stdout.rows ?? 24;
|
|
45
49
|
|
|
46
50
|
const command = options?.requiresRoot ? `sudo ${cmd}` : cmd;
|
package/src/test-utils.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import { ChildProcess } from 'node:child_process';
|
|
3
3
|
import { Readable } from 'stream';
|
|
4
|
-
import {
|
|
4
|
+
import { TestUtils } from './test-utils.js';
|
|
5
5
|
import { describe, expect, it, vi } from 'vitest';
|
|
6
6
|
import { MessageStatus } from 'codify-schemas';
|
|
7
7
|
import { nanoid } from 'nanoid';
|
|
@@ -22,7 +22,7 @@ describe('Test Utils tests', async () => {
|
|
|
22
22
|
const sendMock = vi.spyOn(process, 'send');
|
|
23
23
|
const requestId = nanoid(6);
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
TestUtils.sendMessageAndAwaitResponse(process, { cmd: 'message', data: 'data', requestId })
|
|
26
26
|
|
|
27
27
|
expect(sendMock.mock.calls.length).to.eq(1);
|
|
28
28
|
expect(sendMock.mock.calls[0][0]).to.deep.eq({ cmd: 'message', data: 'data', requestId });
|
|
@@ -38,7 +38,7 @@ describe('Test Utils tests', async () => {
|
|
|
38
38
|
// Note that the response must end in _Response. In accordance to the message schema rules.
|
|
39
39
|
process.emit('message', { cmd: 'message_Response', status: MessageStatus.SUCCESS, data: 'data', requestId })
|
|
40
40
|
})(),
|
|
41
|
-
|
|
41
|
+
TestUtils.sendMessageAndAwaitResponse(process, { cmd: 'message', data: 'data', requestId }),
|
|
42
42
|
]);
|
|
43
43
|
|
|
44
44
|
expect(result[1]).to.eq('data')
|
|
@@ -54,7 +54,7 @@ describe('Test Utils tests', async () => {
|
|
|
54
54
|
// Note that the response must end in _Response. In accordance to the message schema rules.
|
|
55
55
|
process.emit('message', { cmd: 'message_Response', status: MessageStatus.ERROR, data: 'error message', requestId })
|
|
56
56
|
})(),
|
|
57
|
-
|
|
57
|
+
TestUtils.sendMessageAndAwaitResponse(process, { cmd: 'message', data: 'data', requestId }),
|
|
58
58
|
])).rejects.toThrowError(new Error('error message'))
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -70,7 +70,7 @@ describe('Test Utils tests', async () => {
|
|
|
70
70
|
process.emit('message', { cmd: 'randomMessage2', status: MessageStatus.SUCCESS, data: 'message2', requestId: nanoid(6) })
|
|
71
71
|
process.emit('message', { cmd: 'message_Response', status: MessageStatus.SUCCESS, data: 'data', requestId })
|
|
72
72
|
})(),
|
|
73
|
-
|
|
73
|
+
TestUtils.sendMessageAndAwaitResponse(process, { cmd: 'message', data: 'data', requestId }),
|
|
74
74
|
]);
|
|
75
75
|
|
|
76
76
|
// Only the final _Response message should be returned.
|
package/src/test-utils.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import Ajv from 'ajv';
|
|
2
|
-
import { IpcMessageSchema, IpcMessageV2, MessageStatus } from 'codify-schemas';
|
|
2
|
+
import { IpcMessageSchema, IpcMessageV2, MessageStatus, ResourceOs, SpawnStatus } from 'codify-schemas';
|
|
3
3
|
import { ChildProcess } from 'node:child_process';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
import { PluginTester } from './plugin-tester.js';
|
|
8
|
+
import { testSpawn } from './spawn.js';
|
|
4
9
|
|
|
5
10
|
const ajv = new Ajv.default({
|
|
6
11
|
strict: true
|
|
7
12
|
});
|
|
8
13
|
const ipcMessageValidator = ajv.compile(IpcMessageSchema);
|
|
9
14
|
|
|
10
|
-
export const
|
|
15
|
+
export const TestUtils = {
|
|
11
16
|
sendMessageAndAwaitResponse(process: ChildProcess, message: IpcMessageV2): Promise<any> {
|
|
12
17
|
return new Promise((resolve, reject) => {
|
|
13
18
|
process.on('message', (response: IpcMessageV2) => {
|
|
@@ -30,4 +35,113 @@ export const CodifyTestUtils = {
|
|
|
30
35
|
});
|
|
31
36
|
},
|
|
32
37
|
|
|
38
|
+
async ensureHomebrewInstalledOnMacOs(pluginPath: string) {
|
|
39
|
+
const homebrewQuery = await testSpawn('which homebrew');
|
|
40
|
+
if (homebrewQuery.status !== SpawnStatus.SUCCESS) {
|
|
41
|
+
await PluginTester.install(pluginPath, [{ type: 'homebrew', os: [ResourceOs.MACOS] }])
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
async ensureXcodeInstalledOnMacOs(pluginPath: string) {
|
|
46
|
+
const xcodeQuery = await testSpawn('xcodebuild -version');
|
|
47
|
+
if (xcodeQuery.status !== SpawnStatus.SUCCESS) {
|
|
48
|
+
await PluginTester.install(pluginPath, [{ type: 'xcode-tools', os: [ResourceOs.MACOS] }])
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
getShell(): 'bash' | 'zsh' {
|
|
54
|
+
const shell = process.env.SHELL || '';
|
|
55
|
+
|
|
56
|
+
if (shell.includes('bash')) {
|
|
57
|
+
return 'bash';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (shell.includes('zsh')) {
|
|
61
|
+
return 'zsh';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Default to bash for tests
|
|
65
|
+
return 'bash';
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get the primary shell rc file path
|
|
70
|
+
*/
|
|
71
|
+
getPrimaryShellRc(): string {
|
|
72
|
+
const shell = TestUtils.getShell();
|
|
73
|
+
const homeDir = os.homedir();
|
|
74
|
+
|
|
75
|
+
if (shell === 'bash') {
|
|
76
|
+
return path.join(homeDir, '.bashrc')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (shell === 'zsh') {
|
|
80
|
+
return path.join(homeDir, '.zshrc');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
throw new Error('Unsupported shell')
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get the source command for the shell rc file
|
|
88
|
+
* Usage: execSync(TestUtils.getSourceCommand())
|
|
89
|
+
*/
|
|
90
|
+
getSourceCommand(): string {
|
|
91
|
+
return `source ${TestUtils.getPrimaryShellRc()}`;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get shell-specific command to run with sourced environment
|
|
96
|
+
* Usage: execSync(TestUtils.getShellCommand('which brew'))
|
|
97
|
+
*/
|
|
98
|
+
getShellCommand(command: string): string {
|
|
99
|
+
return `${TestUtils.getSourceCommand()}; ${command}`;
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get shell name for execSync shell option
|
|
104
|
+
*/
|
|
105
|
+
getShellName(): string {
|
|
106
|
+
return TestUtils.getShell();
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get interactive shell command
|
|
111
|
+
* Usage: execSync(TestUtils.getInteractiveCommand('my-alias'))
|
|
112
|
+
*/
|
|
113
|
+
getInteractiveCommand(command: string): string {
|
|
114
|
+
const shell = TestUtils.getShell();
|
|
115
|
+
|
|
116
|
+
return shell === 'bash'
|
|
117
|
+
? `bash -i -c "${command}"`
|
|
118
|
+
: `zsh -i -c "${command}"`;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get which command output format based on shell
|
|
123
|
+
*/
|
|
124
|
+
getAliasWhichCommand(aliasName: string): string {
|
|
125
|
+
const shell = TestUtils.getShell();
|
|
126
|
+
|
|
127
|
+
// zsh outputs: "alias_name: aliased to command"
|
|
128
|
+
// bash outputs: "alias alias_name='command'"
|
|
129
|
+
return shell === 'bash'
|
|
130
|
+
? `${TestUtils.getShellCommand(`alias ${aliasName}`)}`
|
|
131
|
+
: `${TestUtils.getShellCommand(`which ${aliasName}`)}`;
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Check if running on macOS
|
|
136
|
+
*/
|
|
137
|
+
isMacOS(): boolean {
|
|
138
|
+
return os.platform() === 'darwin';
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check if running on Linux
|
|
143
|
+
*/
|
|
144
|
+
isLinux(): boolean {
|
|
145
|
+
return os.platform() === 'linux';
|
|
146
|
+
},
|
|
33
147
|
};
|
package/src/utils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ResourceConfig, StringIndexedObject } from 'codify-schemas';
|
|
1
|
+
import { ResourceConfig, ResourceOs, StringIndexedObject } from 'codify-schemas';
|
|
2
|
+
import os from 'node:os';
|
|
2
3
|
|
|
3
4
|
export function splitUserConfig<T extends StringIndexedObject>(
|
|
4
5
|
config: ResourceConfig & T
|
|
@@ -7,13 +8,35 @@ export function splitUserConfig<T extends StringIndexedObject>(
|
|
|
7
8
|
type: config.type,
|
|
8
9
|
...(config.name ? { name: config.name } : {}),
|
|
9
10
|
...(config.dependsOn ? { dependsOn: config.dependsOn } : {}),
|
|
11
|
+
...(config.os ? { os: config.os } : {}),
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
13
|
-
const { type, name, dependsOn, ...parameters } = config;
|
|
15
|
+
const { type, name, dependsOn, os, ...parameters } = config;
|
|
14
16
|
|
|
15
17
|
return {
|
|
16
18
|
parameters: parameters as T,
|
|
17
19
|
coreParameters,
|
|
18
20
|
};
|
|
19
21
|
}
|
|
22
|
+
|
|
23
|
+
export function getPlatformOs(): ResourceOs{
|
|
24
|
+
const currOs = os.platform();
|
|
25
|
+
switch (currOs) {
|
|
26
|
+
case 'darwin': {
|
|
27
|
+
return ResourceOs.MACOS;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
case 'linux': {
|
|
31
|
+
return ResourceOs.LINUX;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
case 'win32': {
|
|
35
|
+
return ResourceOs.WINDOWS;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
default: {
|
|
39
|
+
throw new Error(`Unsupported OS: ${currOs}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -27,14 +27,14 @@ describe('Plugin tester integration tests', () => {
|
|
|
27
27
|
|
|
28
28
|
const result = await plugin.initialize();
|
|
29
29
|
expect(result).toMatchObject({
|
|
30
|
-
resourceDefinitions: [
|
|
31
|
-
{ dependencies: [], type: 'test' },
|
|
32
|
-
{ dependencies: [], type: 'test2' },
|
|
33
|
-
{ dependencies: [], type: 'test-uninstall' },
|
|
34
|
-
{ dependencies: [], type: 'test-modify' },
|
|
35
|
-
{ dependencies: [], type: 'test-destroy' },
|
|
36
|
-
{ dependencies: [], type: 'test-destroy-2' }
|
|
37
|
-
]
|
|
30
|
+
resourceDefinitions: expect.arrayContaining([
|
|
31
|
+
expect.objectContaining({ dependencies: [], type: 'test' }),
|
|
32
|
+
expect.objectContaining({ dependencies: [], type: 'test2' }),
|
|
33
|
+
expect.objectContaining({ dependencies: [], type: 'test-uninstall' }),
|
|
34
|
+
expect.objectContaining({ dependencies: [], type: 'test-modify' }),
|
|
35
|
+
expect.objectContaining({ dependencies: [], type: 'test-destroy' }),
|
|
36
|
+
expect.objectContaining({ dependencies: [], type: 'test-destroy-2' }),
|
|
37
|
+
])
|
|
38
38
|
})
|
|
39
39
|
})
|
|
40
40
|
|
|
@@ -313,6 +313,17 @@ describe('Plugin tester integration tests', () => {
|
|
|
313
313
|
}
|
|
314
314
|
})
|
|
315
315
|
|
|
316
|
+
it('Can filter out unsupported configs based on OS', { timeout: 300000 }, async () => {
|
|
317
|
+
await PluginTester.fullTest(pluginPath, [{
|
|
318
|
+
type: 'windows-only',
|
|
319
|
+
}], {
|
|
320
|
+
validatePlan(plan) {
|
|
321
|
+
expect(plan.length).to.eq(0);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
|
|
316
327
|
|
|
317
328
|
// it('Can uninstall two resources with the same type', async () => {
|
|
318
329
|
// await plugin.fullTest([{
|