The test called runGsdTools with its default cwd — the test process's own working directory — and an inherited HOME, so the child read the checkout's real .planning/ and the developer's real ~/.gsd/defaults.json. testEnvBase() blanks the config-LOCATION env keys but sandboxes neither cwd nor HOME. On a checkout that has workstreams with no active pointer, `init.progress` exits non-zero and the FIRST assertion fails, so the routing comparison the test exists for was never evaluated: init.progress failed: Error: init.progress requires a workstream in workstream mode — no active workstream is set ... Available workstreams: alpha Reproduced byte-for-byte by adding .planning/workstreams/alpha/ to the checkout: red on next, green here. The invariant under test — `query <cmd>` and `<cmd>` returning identical payloads — is independent of project state, so a plain createTempProject() fixture is enough, with HOME/USERPROFILE pointed at it (the idiom runGsdTools's own doc comment prescribes). Two assertions pin the sandbox deterministically rather than conditionally: the fixture HAS a .planning/ and the repo checkout does not, so dropping the cwd override fails on every lane — CI included, where the ambient state that exposed the bug is absent. Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
@@ -962,20 +962,48 @@ describe('CommandRoutingHub — exitReason? field on InvalidArgs (#1644 / amendm
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const { runGsdTools } = require('./helpers.cjs');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
test('bug #167: query meta-command prefixes direct gsd-tools calls', () => {
|
||||
const direct = runGsdTools(['init.progress']);
|
||||
// #4342: this ran gsd-tools with runGsdTools's default cwd — the checkout's own
|
||||
// working directory — and an inherited HOME, so the child read the developer's
|
||||
// real .planning/config.json and ~/.gsd/defaults.json. On a machine whose real
|
||||
// project enables workstream mode with no active workstream, `init.progress`
|
||||
// exits non-zero and the FIRST assertion fails, so the routing comparison this
|
||||
// test exists for was never reached. testEnvBase() blanks the config-LOCATION
|
||||
// env keys but sandboxes neither cwd nor HOME.
|
||||
//
|
||||
// The routing invariant is about `query <cmd>` and `<cmd>` agreeing, which is
|
||||
// independent of project state — so the fixture only has to be a project whose
|
||||
// state is known and unaffected by the developer's.
|
||||
test('bug #167: query meta-command prefixes direct gsd-tools calls', (t) => {
|
||||
const fixture = createTempProject('gsd-4342-routing-');
|
||||
t.after(() => cleanup(fixture));
|
||||
// HOME/USERPROFILE point at the fixture so ~/.gsd/defaults.json resolves
|
||||
// inside it (absent) rather than in the developer's home — the idiom
|
||||
// runGsdTools's own doc comment prescribes for exactly this.
|
||||
const sandbox = { HOME: fixture, USERPROFILE: fixture };
|
||||
|
||||
const direct = runGsdTools(['init.progress'], fixture, sandbox);
|
||||
assert.equal(direct.success, true, `init.progress failed: ${direct.error || direct.output}`);
|
||||
|
||||
const meta = runGsdTools(['query', 'init.progress']);
|
||||
const meta = runGsdTools(['query', 'init.progress'], fixture, sandbox);
|
||||
assert.equal(meta.success, true, `query init.progress failed: ${meta.error || meta.output}`);
|
||||
|
||||
const directPayload = JSON.parse(direct.output);
|
||||
assert.deepEqual(
|
||||
JSON.parse(meta.output),
|
||||
JSON.parse(direct.output),
|
||||
directPayload,
|
||||
'query-prefixed and direct invocations should return identical init.progress payloads'
|
||||
);
|
||||
|
||||
// Pin the sandbox itself, deterministically rather than conditionally: the
|
||||
// fixture HAS a .planning/ and the repo checkout does NOT, so if the cwd
|
||||
// override is ever dropped this fails on every lane — including CI, where the
|
||||
// ambient state that exposed the bug is absent.
|
||||
assert.equal(directPayload.planning_exists, true,
|
||||
'the child must run in the fixture project, not in the checkout');
|
||||
assert.equal(directPayload.phase_count, 0,
|
||||
'the fixture has no phases — a non-zero count means a real project was read');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user