gsd-opencode 1.20.2 → 1.20.3
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/commands/gsd/gsd-check-profile.md +30 -0
- package/get-shit-done/bin/gsd-oc-commands/check-oc-config-json.cjs +169 -0
- package/get-shit-done/bin/gsd-oc-commands/check-opencode-json.cjs +86 -0
- package/get-shit-done/bin/gsd-oc-commands/get-profile.cjs +117 -0
- package/get-shit-done/bin/gsd-oc-commands/set-profile.cjs +357 -0
- package/get-shit-done/bin/gsd-oc-commands/update-opencode-json.cjs +199 -0
- package/get-shit-done/bin/gsd-oc-commands/validate-models.cjs +75 -0
- package/get-shit-done/bin/gsd-oc-lib/oc-config.cjs +205 -0
- package/get-shit-done/bin/gsd-oc-lib/oc-core.cjs +113 -0
- package/get-shit-done/bin/gsd-oc-lib/oc-models.cjs +133 -0
- package/get-shit-done/bin/gsd-oc-lib/oc-profile-config.cjs +409 -0
- package/get-shit-done/bin/gsd-oc-tools.cjs +130 -0
- package/get-shit-done/bin/lib/oc-config.cjs +200 -0
- package/get-shit-done/bin/lib/oc-core.cjs +114 -0
- package/get-shit-done/bin/lib/oc-models.cjs +133 -0
- package/get-shit-done/bin/test/fixtures/oc-config-invalid.json +14 -0
- package/get-shit-done/bin/test/fixtures/oc-config-valid.json +22 -0
- package/get-shit-done/bin/test/get-profile.test.cjs +447 -0
- package/get-shit-done/bin/test/oc-profile-config.test.cjs +377 -0
- package/get-shit-done/bin/test/pivot-profile.test.cjs +276 -0
- package/get-shit-done/bin/test/set-profile.test.cjs +301 -0
- package/get-shit-done/workflows/oc-check-profile.md +181 -0
- package/get-shit-done/workflows/oc-set-profile.md +83 -243
- package/get-shit-done/workflows/settings.md +4 -3
- package/package.json +2 -2
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for get-profile.cjs command
|
|
3
|
+
*
|
|
4
|
+
* Tests for both operation modes:
|
|
5
|
+
* - Mode 1: No parameters (get current profile)
|
|
6
|
+
* - Mode 2: Profile name parameter (get specific profile)
|
|
7
|
+
*
|
|
8
|
+
* Also tests --raw and --verbose flags, and error handling
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import os from 'os';
|
|
15
|
+
|
|
16
|
+
// Mock console.log and console.error to capture output
|
|
17
|
+
const originalLog = console.log;
|
|
18
|
+
const originalError = console.error;
|
|
19
|
+
const originalExit = process.exit;
|
|
20
|
+
|
|
21
|
+
// Test fixtures
|
|
22
|
+
const VALID_CONFIG_WITH_CURRENT = {
|
|
23
|
+
current_oc_profile: 'smart',
|
|
24
|
+
profiles: {
|
|
25
|
+
presets: {
|
|
26
|
+
simple: {
|
|
27
|
+
planning: 'bailian-coding-plan/qwen3.5-plus',
|
|
28
|
+
execution: 'bailian-coding-plan/qwen3.5-plus',
|
|
29
|
+
verification: 'bailian-coding-plan/qwen3.5-plus'
|
|
30
|
+
},
|
|
31
|
+
smart: {
|
|
32
|
+
planning: 'bailian-coding-plan/qwen3.5-plus',
|
|
33
|
+
execution: 'bailian-coding-plan/qwen3.5-plus',
|
|
34
|
+
verification: 'bailian-coding-plan/qwen3.5-plus'
|
|
35
|
+
},
|
|
36
|
+
genius: {
|
|
37
|
+
planning: 'bailian-coding-plan/qwen3.5-plus',
|
|
38
|
+
execution: 'bailian-coding-plan/qwen3.5-plus',
|
|
39
|
+
verification: 'bailian-coding-plan/qwen3.5-plus'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const VALID_CONFIG_WITHOUT_CURRENT = {
|
|
46
|
+
profiles: {
|
|
47
|
+
presets: {
|
|
48
|
+
simple: {
|
|
49
|
+
planning: 'bailian-coding-plan/qwen3.5-plus',
|
|
50
|
+
execution: 'bailian-coding-plan/qwen3.5-plus',
|
|
51
|
+
verification: 'bailian-coding-plan/qwen3.5-plus'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const VALID_CONFIG_INCOMPLETE_PROFILE = {
|
|
58
|
+
current_oc_profile: 'broken',
|
|
59
|
+
profiles: {
|
|
60
|
+
presets: {
|
|
61
|
+
broken: {
|
|
62
|
+
planning: 'bailian-coding-plan/qwen3.5-plus'
|
|
63
|
+
// missing execution and verification
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
describe('get-profile.cjs', () => {
|
|
70
|
+
let testDir;
|
|
71
|
+
let planningDir;
|
|
72
|
+
let configPath;
|
|
73
|
+
let capturedLog;
|
|
74
|
+
let capturedError;
|
|
75
|
+
let exitCode;
|
|
76
|
+
let allLogs;
|
|
77
|
+
let allErrors;
|
|
78
|
+
|
|
79
|
+
beforeEach(() => {
|
|
80
|
+
// Create isolated test directory
|
|
81
|
+
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'get-profile-test-'));
|
|
82
|
+
planningDir = path.join(testDir, '.planning');
|
|
83
|
+
configPath = path.join(planningDir, 'oc_config.json');
|
|
84
|
+
fs.mkdirSync(planningDir, { recursive: true });
|
|
85
|
+
|
|
86
|
+
// Reset captured output
|
|
87
|
+
capturedLog = null;
|
|
88
|
+
capturedError = null;
|
|
89
|
+
exitCode = null;
|
|
90
|
+
allLogs = [];
|
|
91
|
+
allErrors = [];
|
|
92
|
+
|
|
93
|
+
// Mock console.log to capture all output
|
|
94
|
+
console.log = (msg) => {
|
|
95
|
+
allLogs.push(msg);
|
|
96
|
+
capturedLog = msg;
|
|
97
|
+
};
|
|
98
|
+
console.error = (msg) => {
|
|
99
|
+
allErrors.push(msg);
|
|
100
|
+
capturedError = msg;
|
|
101
|
+
};
|
|
102
|
+
process.exit = (code) => {
|
|
103
|
+
exitCode = code;
|
|
104
|
+
throw new Error(`process.exit(${code})`);
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
afterEach(() => {
|
|
109
|
+
// Restore original functions
|
|
110
|
+
console.log = originalLog;
|
|
111
|
+
console.error = originalError;
|
|
112
|
+
process.exit = originalExit;
|
|
113
|
+
|
|
114
|
+
// Cleanup test directory
|
|
115
|
+
try {
|
|
116
|
+
fs.rmSync(testDir, { recursive: true, force: true });
|
|
117
|
+
} catch (err) {
|
|
118
|
+
// Ignore cleanup errors
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Import getProfile inside tests to use mocked functions
|
|
123
|
+
const importGetProfile = () => {
|
|
124
|
+
// Need to clear cache to get fresh import with mocked dependencies
|
|
125
|
+
const modulePath = '../gsd-oc-commands/get-profile.cjs';
|
|
126
|
+
delete require.cache[require.resolve(modulePath)];
|
|
127
|
+
return require(modulePath);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
describe('Mode 1: No parameters (get current profile)', () => {
|
|
131
|
+
it('returns current profile when current_oc_profile is set', () => {
|
|
132
|
+
// Write config with current_oc_profile
|
|
133
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
|
134
|
+
|
|
135
|
+
const getProfile = importGetProfile();
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
getProfile(testDir, []);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
// Expected to throw due to process.exit mock
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
expect(exitCode).toBe(0);
|
|
144
|
+
const output = JSON.parse(capturedLog);
|
|
145
|
+
expect(output.success).toBe(true);
|
|
146
|
+
expect(output.data).toHaveProperty('smart');
|
|
147
|
+
expect(output.data.smart).toEqual(VALID_CONFIG_WITH_CURRENT.profiles.presets.smart);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('returns MISSING_CURRENT_PROFILE error when current_oc_profile not set', () => {
|
|
151
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITHOUT_CURRENT, null, 2));
|
|
152
|
+
|
|
153
|
+
const getProfile = importGetProfile();
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
getProfile(testDir, []);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
// Expected
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
expect(exitCode).toBe(1);
|
|
162
|
+
const output = JSON.parse(capturedError);
|
|
163
|
+
expect(output.success).toBe(false);
|
|
164
|
+
expect(output.error.code).toBe('MISSING_CURRENT_PROFILE');
|
|
165
|
+
expect(output.error.message).toContain('current_oc_profile not set');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('returns PROFILE_NOT_FOUND when current profile does not exist', () => {
|
|
169
|
+
const configWithMissingProfile = {
|
|
170
|
+
current_oc_profile: 'nonexistent',
|
|
171
|
+
profiles: {
|
|
172
|
+
presets: {
|
|
173
|
+
smart: {
|
|
174
|
+
planning: 'bailian-coding-plan/qwen3.5-plus',
|
|
175
|
+
execution: 'bailian-coding-plan/qwen3.5-plus',
|
|
176
|
+
verification: 'bailian-coding-plan/qwen3.5-plus'
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
fs.writeFileSync(configPath, JSON.stringify(configWithMissingProfile, null, 2));
|
|
182
|
+
|
|
183
|
+
const getProfile = importGetProfile();
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
getProfile(testDir, []);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
// Expected
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
expect(exitCode).toBe(1);
|
|
192
|
+
const output = JSON.parse(capturedError);
|
|
193
|
+
expect(output.success).toBe(false);
|
|
194
|
+
expect(output.error.code).toBe('PROFILE_NOT_FOUND');
|
|
195
|
+
expect(output.error.message).toContain('nonexistent');
|
|
196
|
+
expect(output.error.message).toContain('smart');
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('Mode 2: Profile name parameter (get specific profile)', () => {
|
|
201
|
+
it('returns specified profile when it exists', () => {
|
|
202
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
|
203
|
+
|
|
204
|
+
const getProfile = importGetProfile();
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
getProfile(testDir, ['genius']);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
// Expected
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
expect(exitCode).toBe(0);
|
|
213
|
+
const output = JSON.parse(capturedLog);
|
|
214
|
+
expect(output.success).toBe(true);
|
|
215
|
+
expect(output.data).toHaveProperty('genius');
|
|
216
|
+
expect(output.data.genius).toEqual(VALID_CONFIG_WITH_CURRENT.profiles.presets.genius);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('works even when current_oc_profile is not set', () => {
|
|
220
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITHOUT_CURRENT, null, 2));
|
|
221
|
+
|
|
222
|
+
const getProfile = importGetProfile();
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
getProfile(testDir, ['simple']);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
// Expected
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
expect(exitCode).toBe(0);
|
|
231
|
+
const output = JSON.parse(capturedLog);
|
|
232
|
+
expect(output.success).toBe(true);
|
|
233
|
+
expect(output.data).toHaveProperty('simple');
|
|
234
|
+
expect(output.data.simple).toEqual(VALID_CONFIG_WITHOUT_CURRENT.profiles.presets.simple);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('returns PROFILE_NOT_FOUND with available profiles when profile does not exist', () => {
|
|
238
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
|
239
|
+
|
|
240
|
+
const getProfile = importGetProfile();
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
getProfile(testDir, ['nonexistent']);
|
|
244
|
+
} catch (err) {
|
|
245
|
+
// Expected
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
expect(exitCode).toBe(1);
|
|
249
|
+
const output = JSON.parse(capturedError);
|
|
250
|
+
expect(output.success).toBe(false);
|
|
251
|
+
expect(output.error.code).toBe('PROFILE_NOT_FOUND');
|
|
252
|
+
expect(output.error.message).toContain('nonexistent');
|
|
253
|
+
expect(output.error.message).toContain('simple');
|
|
254
|
+
expect(output.error.message).toContain('smart');
|
|
255
|
+
expect(output.error.message).toContain('genius');
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe('--raw flag', () => {
|
|
260
|
+
it('outputs raw JSON without envelope in Mode 1', () => {
|
|
261
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
|
262
|
+
|
|
263
|
+
const getProfile = importGetProfile();
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
getProfile(testDir, ['--raw']);
|
|
267
|
+
} catch (err) {
|
|
268
|
+
// Expected
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
expect(exitCode).toBe(0);
|
|
272
|
+
// capturedLog is already a string from JSON.stringify
|
|
273
|
+
const output = JSON.parse(capturedLog);
|
|
274
|
+
// Raw output should NOT have success/data envelope
|
|
275
|
+
expect(output).not.toHaveProperty('success');
|
|
276
|
+
expect(output).toHaveProperty('smart');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('outputs raw JSON without envelope in Mode 2', () => {
|
|
280
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
|
281
|
+
|
|
282
|
+
const getProfile = importGetProfile();
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
getProfile(testDir, ['genius', '--raw']);
|
|
286
|
+
} catch (err) {
|
|
287
|
+
// Expected
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
expect(exitCode).toBe(0);
|
|
291
|
+
const output = JSON.parse(capturedLog);
|
|
292
|
+
expect(output).not.toHaveProperty('success');
|
|
293
|
+
expect(output).toHaveProperty('genius');
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
describe('--verbose flag', () => {
|
|
298
|
+
it('outputs diagnostic info to stderr in Mode 1', () => {
|
|
299
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
|
300
|
+
|
|
301
|
+
const getProfile = importGetProfile();
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
getProfile(testDir, ['--verbose']);
|
|
305
|
+
} catch (err) {
|
|
306
|
+
// Expected
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
expect(exitCode).toBe(0);
|
|
310
|
+
expect(allErrors.length).toBeGreaterThan(0);
|
|
311
|
+
// Check if any error message contains the expected content
|
|
312
|
+
const hasVerboseOutput = allErrors.some(msg => msg.includes('[get-profile]'));
|
|
313
|
+
expect(hasVerboseOutput).toBe(true);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('outputs diagnostic info to stderr in Mode 2', () => {
|
|
317
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
|
318
|
+
|
|
319
|
+
const getProfile = importGetProfile();
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
getProfile(testDir, ['genius', '--verbose']);
|
|
323
|
+
} catch (err) {
|
|
324
|
+
// Expected
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
expect(exitCode).toBe(0);
|
|
328
|
+
// Verbose output is sent to console.error, check if any errors were logged
|
|
329
|
+
expect(allErrors.length).toBeGreaterThan(0);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
describe('Error format', () => {
|
|
334
|
+
it('uses structured JSON error format for CONFIG_NOT_FOUND', () => {
|
|
335
|
+
// Don't create config file
|
|
336
|
+
const getProfile = importGetProfile();
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
getProfile(testDir, []);
|
|
340
|
+
} catch (err) {
|
|
341
|
+
// Expected
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
expect(exitCode).toBe(1);
|
|
345
|
+
const output = JSON.parse(capturedError);
|
|
346
|
+
expect(output.success).toBe(false);
|
|
347
|
+
expect(output.error).toHaveProperty('code');
|
|
348
|
+
expect(output.error).toHaveProperty('message');
|
|
349
|
+
expect(output.error.code).toBe('CONFIG_NOT_FOUND');
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('uses structured JSON error format for INVALID_JSON', () => {
|
|
353
|
+
fs.writeFileSync(configPath, 'invalid json {');
|
|
354
|
+
|
|
355
|
+
const getProfile = importGetProfile();
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
getProfile(testDir, []);
|
|
359
|
+
} catch (err) {
|
|
360
|
+
// Expected
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
expect(exitCode).toBe(1);
|
|
364
|
+
const output = JSON.parse(capturedError);
|
|
365
|
+
expect(output.success).toBe(false);
|
|
366
|
+
expect(output.error.code).toBe('INVALID_JSON');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('provides descriptive error messages', () => {
|
|
370
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITHOUT_CURRENT, null, 2));
|
|
371
|
+
|
|
372
|
+
const getProfile = importGetProfile();
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
getProfile(testDir, []);
|
|
376
|
+
} catch (err) {
|
|
377
|
+
// Expected
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
expect(exitCode).toBe(1);
|
|
381
|
+
const output = JSON.parse(capturedError);
|
|
382
|
+
expect(output.error.message).toContain('current_oc_profile');
|
|
383
|
+
expect(output.error.message).toContain('Run set-profile first');
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
describe('Output format', () => {
|
|
388
|
+
it('returns profile definition as {profileName: {planning, execution, verification}}', () => {
|
|
389
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
|
390
|
+
|
|
391
|
+
const getProfile = importGetProfile();
|
|
392
|
+
|
|
393
|
+
try {
|
|
394
|
+
getProfile(testDir, ['smart']);
|
|
395
|
+
} catch (err) {
|
|
396
|
+
// Expected
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
expect(exitCode).toBe(0);
|
|
400
|
+
const output = JSON.parse(capturedLog);
|
|
401
|
+
expect(output.data).toHaveProperty('smart');
|
|
402
|
+
expect(output.data.smart).toHaveProperty('planning');
|
|
403
|
+
expect(output.data.smart).toHaveProperty('execution');
|
|
404
|
+
expect(output.data.smart).toHaveProperty('verification');
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
describe('Error handling', () => {
|
|
409
|
+
it('handles missing .planning directory', () => {
|
|
410
|
+
const badTestDir = fs.mkdtempSync(path.join(os.tmpdir(), 'get-profile-test-'));
|
|
411
|
+
// Don't create .planning directory
|
|
412
|
+
|
|
413
|
+
const getProfile = importGetProfile();
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
getProfile(badTestDir, []);
|
|
417
|
+
} catch (err) {
|
|
418
|
+
// Expected
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
expect(exitCode).toBe(1);
|
|
422
|
+
const output = JSON.parse(capturedError);
|
|
423
|
+
expect(output.success).toBe(false);
|
|
424
|
+
expect(output.error.code).toBe('CONFIG_NOT_FOUND');
|
|
425
|
+
|
|
426
|
+
fs.rmSync(badTestDir, { recursive: true, force: true });
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('handles too many arguments', () => {
|
|
430
|
+
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
|
431
|
+
|
|
432
|
+
const getProfile = importGetProfile();
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
getProfile(testDir, ['smart', 'genius']);
|
|
436
|
+
} catch (err) {
|
|
437
|
+
// Expected
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
expect(exitCode).toBe(1);
|
|
441
|
+
const output = JSON.parse(capturedError);
|
|
442
|
+
expect(output.success).toBe(false);
|
|
443
|
+
expect(output.error.code).toBe('INVALID_ARGS');
|
|
444
|
+
expect(output.error.message).toContain('Too many arguments');
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
});
|