From 2c42f518387fa550b41b453f4234214588d8fd9a Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Sun, 29 Mar 2026 14:41:58 -0700 Subject: [PATCH] test(config): add automated tests for context_window and subagent_timeout Add tests covering the three manual verification scenarios for the context_window and workflow.subagent_timeout config features: - init execute-phase output includes context_window from config (both custom 1M value and 200k default) - config-get context_window returns the configured value (and errors when absent) - config-set workflow.subagent_timeout accepts numeric values with proper string-to-number coercion and round-trips through config-get All 1517 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/subagent-timeout.test.cjs | 129 ++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/tests/subagent-timeout.test.cjs b/tests/subagent-timeout.test.cjs index e6ea5b07c..fd16ded45 100644 --- a/tests/subagent-timeout.test.cjs +++ b/tests/subagent-timeout.test.cjs @@ -123,3 +123,132 @@ describe('planning-config.md documents subagent_timeout (#1472)', () => { ); }); }); + +// ─── init execute-phase includes context_window ───────────────────────────── + +describe('init execute-phase context_window (#1472)', () => { + let tmpDir; + + beforeEach(() => { + tmpDir = createTempProject(); + }); + + afterEach(() => { + cleanup(tmpDir); + }); + + test('init execute-phase output includes context_window from config', () => { + // Write config with a custom context_window value (1M for Opus/Sonnet 4.6) + const configPath = path.join(tmpDir, '.planning', 'config.json'); + fs.writeFileSync(configPath, JSON.stringify({ + context_window: 1000000, + }, null, 2)); + + // Create a phase directory with a plan so init execute-phase succeeds + const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); + fs.mkdirSync(phaseDir, { recursive: true }); + fs.writeFileSync(path.join(phaseDir, '01-01-PLAN.md'), '# Plan'); + + const result = runGsdTools('init execute-phase 1', tmpDir, { HOME: tmpDir }); + assert.ok(result.success, `Command failed: ${result.error}`); + + const output = JSON.parse(result.output); + assert.strictEqual(output.context_window, 1000000, 'context_window should reflect configured value'); + }); + + test('init execute-phase uses default context_window when not configured', () => { + // Write minimal config without context_window + const configPath = path.join(tmpDir, '.planning', 'config.json'); + fs.writeFileSync(configPath, JSON.stringify({}, null, 2)); + + const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); + fs.mkdirSync(phaseDir, { recursive: true }); + fs.writeFileSync(path.join(phaseDir, '01-01-PLAN.md'), '# Plan'); + + const result = runGsdTools('init execute-phase 1', tmpDir, { HOME: tmpDir }); + assert.ok(result.success, `Command failed: ${result.error}`); + + const output = JSON.parse(result.output); + assert.strictEqual(output.context_window, 200000, 'default context_window should be 200000'); + }); +}); + +// ─── config-get context_window ────────────────────────────────────────────── + +describe('config-get context_window (#1472)', () => { + let tmpDir; + + beforeEach(() => { + tmpDir = createTempProject(); + }); + + afterEach(() => { + cleanup(tmpDir); + }); + + test('config-get context_window returns the configured value', () => { + const configPath = path.join(tmpDir, '.planning', 'config.json'); + fs.writeFileSync(configPath, JSON.stringify({ + context_window: 1000000, + }, null, 2)); + + const result = runGsdTools('config-get context_window', tmpDir); + assert.ok(result.success, `Command failed: ${result.error}`); + + const output = JSON.parse(result.output); + assert.strictEqual(output, 1000000); + }); + + test('config-get context_window errors when key is absent', () => { + const configPath = path.join(tmpDir, '.planning', 'config.json'); + fs.writeFileSync(configPath, JSON.stringify({}, null, 2)); + + const result = runGsdTools('config-get context_window', tmpDir); + assert.strictEqual(result.success, false); + assert.ok( + result.error.includes('Key not found'), + `Expected "Key not found" in error: ${result.error}` + ); + }); +}); + +// ─── config-set workflow.subagent_timeout numeric coercion ────────────────── + +describe('config-set workflow.subagent_timeout numeric values (#1472)', () => { + let tmpDir; + + beforeEach(() => { + tmpDir = createTempProject(); + const configPath = path.join(tmpDir, '.planning', 'config.json'); + fs.writeFileSync(configPath, JSON.stringify({}, null, 2)); + }); + + afterEach(() => { + cleanup(tmpDir); + }); + + test('config-set workflow.subagent_timeout coerces string to number', () => { + const result = runGsdTools(['config-set', 'workflow.subagent_timeout', '900000'], tmpDir); + assert.ok(result.success, `Command failed: ${result.error}`); + + const output = JSON.parse(result.output); + assert.strictEqual(output.updated, true); + assert.strictEqual(output.key, 'workflow.subagent_timeout'); + assert.strictEqual(output.value, 900000); + + const configPath = path.join(tmpDir, '.planning', 'config.json'); + const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); + assert.strictEqual(config.workflow.subagent_timeout, 900000); + assert.strictEqual(typeof config.workflow.subagent_timeout, 'number'); + }); + + test('config-set workflow.subagent_timeout round-trips through config-get', () => { + runGsdTools(['config-set', 'workflow.subagent_timeout', '1200000'], tmpDir); + + const result = runGsdTools('config-get workflow.subagent_timeout', tmpDir); + assert.ok(result.success, `Command failed: ${result.error}`); + + const output = JSON.parse(result.output); + assert.strictEqual(output, 1200000); + }); +});