mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
Compare commits
1 Commits
fix/2519-s
...
fix/2544-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
726003563a |
@@ -58,6 +58,43 @@ describe('configGet', () => {
|
||||
await expect(configGet(['nonexistent.key'], tmpDir)).rejects.toThrow(GSDError);
|
||||
});
|
||||
|
||||
it('throws GSDError with Execution classification for missing key (exit code 1 not 10)', async () => {
|
||||
// Regression for #2544: missing key must exit 1, not 10 (Validation).
|
||||
// Callers like `gsd-sdk query config-get k || default` rely on non-zero exit.
|
||||
// ErrorClassification.Execution maps to exit code 1 (matches git config --get).
|
||||
const { configGet } = await import('./config-query.js');
|
||||
const { ErrorClassification } = await import('../errors.js');
|
||||
await writeFile(
|
||||
join(tmpDir, '.planning', 'config.json'),
|
||||
JSON.stringify({ model_profile: 'quality' }),
|
||||
);
|
||||
let thrown: unknown;
|
||||
try {
|
||||
await configGet(['nonexistent.key'], tmpDir);
|
||||
} catch (err) {
|
||||
thrown = err;
|
||||
}
|
||||
expect(thrown).toBeInstanceOf(GSDError);
|
||||
expect((thrown as GSDError).classification).toBe(ErrorClassification.Execution);
|
||||
});
|
||||
|
||||
it('throws GSDError with Execution classification when traversal hits non-object', async () => {
|
||||
const { configGet } = await import('./config-query.js');
|
||||
const { ErrorClassification } = await import('../errors.js');
|
||||
await writeFile(
|
||||
join(tmpDir, '.planning', 'config.json'),
|
||||
JSON.stringify({ workflow: 'a-string-not-an-object' }),
|
||||
);
|
||||
let thrown: unknown;
|
||||
try {
|
||||
await configGet(['workflow.auto_advance'], tmpDir);
|
||||
} catch (err) {
|
||||
thrown = err;
|
||||
}
|
||||
expect(thrown).toBeInstanceOf(GSDError);
|
||||
expect((thrown as GSDError).classification).toBe(ErrorClassification.Execution);
|
||||
});
|
||||
|
||||
it('reads raw config without merging defaults', async () => {
|
||||
const { configGet } = await import('./config-query.js');
|
||||
// Write config with only model_profile -- no workflow section
|
||||
|
||||
@@ -90,12 +90,12 @@ export const configGet: QueryHandler = async (args, projectDir) => {
|
||||
let current: unknown = config;
|
||||
for (const key of keys) {
|
||||
if (current === undefined || current === null || typeof current !== 'object') {
|
||||
throw new GSDError(`Key not found: ${keyPath}`, ErrorClassification.Validation);
|
||||
throw new GSDError(`Key not found: ${keyPath}`, ErrorClassification.Execution);
|
||||
}
|
||||
current = (current as Record<string, unknown>)[key];
|
||||
}
|
||||
if (current === undefined) {
|
||||
throw new GSDError(`Key not found: ${keyPath}`, ErrorClassification.Validation);
|
||||
throw new GSDError(`Key not found: ${keyPath}`, ErrorClassification.Execution);
|
||||
}
|
||||
|
||||
return { data: current };
|
||||
|
||||
44
tests/bug-2544-config-get-exit-code.test.cjs
Normal file
44
tests/bug-2544-config-get-exit-code.test.cjs
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Bug #2544: `gsd-sdk query config-get` exits 0 on missing key.
|
||||
*
|
||||
* Callers that use `gsd-sdk query config-get k || fallback` rely on a
|
||||
* non-zero exit code when the key is absent. The fix changes the
|
||||
* ErrorClassification for "Key not found" from Validation (exit 10)
|
||||
* to Execution (exit 1), matching the UNIX convention of `git config --get`.
|
||||
*/
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const CONFIG_QUERY_SRC = path.join(
|
||||
__dirname, '..', 'sdk', 'src', 'query', 'config-query.ts',
|
||||
);
|
||||
|
||||
describe('gsd-sdk config-get exit code for missing key (#2544)', () => {
|
||||
test('config-query.ts source exists', () => {
|
||||
assert.ok(fs.existsSync(CONFIG_QUERY_SRC), 'sdk/src/query/config-query.ts must exist');
|
||||
});
|
||||
|
||||
test('"Key not found" throws with Execution classification, not Validation', () => {
|
||||
const src = fs.readFileSync(CONFIG_QUERY_SRC, 'utf-8');
|
||||
// Find the "Key not found" throw lines and confirm they use Execution, not Validation
|
||||
const keyNotFoundLines = src
|
||||
.split('\n')
|
||||
.filter(line => line.includes('Key not found'));
|
||||
assert.ok(keyNotFoundLines.length > 0, 'Source must contain "Key not found" throw(s)');
|
||||
for (const line of keyNotFoundLines) {
|
||||
assert.ok(
|
||||
line.includes('Execution'),
|
||||
`"Key not found" throw must use ErrorClassification.Execution (exit 1), got: ${line.trim()}`
|
||||
);
|
||||
assert.ok(
|
||||
!line.includes('Validation'),
|
||||
`"Key not found" throw must NOT use ErrorClassification.Validation (exit 10), got: ${line.trim()}`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user