* test(#4298): add tdd-walk.cjs end-to-end sniff-test harness for TDD dispatch Epic #4272 Phase 5's own checklist named this deliverable ("the same class of coverage loop-walk.cjs gives the loop") separately from #4268. Adds tests/qa/tdd-walk.cjs, extracting and REALLY EXECUTING (via a real `bash -c` subprocess against a real temp fixture project) the shipped bash resolution snippets from both TDD dispatch backends — never reimplementing or grep-simulating the predicate. Proves, by execution rather than text-shape assertion: the CLI predicate and both backends agree for a type: tdd plan and a plain plan; the worktree backend's fail-closed guard genuinely halts (non-zero exit, FATAL stderr) on a missing plan file; and the tdd.md embed ternary's condition tracks the real resolved value (#3800). This is exactly the class of proof #4264/#4265 (unassigned/divergent predicate) and #4268 (static-shape checks can't see backend divergence) could not provide. Extraction uses indexOf/slice on fenced-code markers only, never a backtracking regex over whole-file text (per the #4228 incident this repo's tests already document). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(#4298): scrub ambient env, tighten fail-closed assertion, fix comment Standards+Spec review found: (1) executeBackendScript spread raw process.env unfiltered into the spawned bash subprocess, unlike tests/helpers.cjs's runGsdTools, which deliberately scrubs SESSION_IDENTITY_ENV_KEYS + config-location env vars before spawning (#2665) — an ambient developer/CI override could silently change what phase.tdd-applicable resolves to in a way a gsd-test bench container won't reproduce; (2) the row-5 fail-closed test asserted only `stderr.includes('FATAL')`, which would also pass if the file's unrelated ISOLATION fail-closed guard fired instead of the TDD one; (3) a docstring called the worktree backend's first fenced block a "shim preamble" when it's actually the whole ISOLATION-resolution block. Fixes: spread the exported TEST_ENV_BASE (every scrub-listed key set to '') before the two intentional RUNTIME_DIR/GSD_TEST_MODE overrides; assert the exact TDD-applicability FATAL text; correct the docstring. Re-verified by direct execution against real fixtures — all three precedence-tier cases and the fail-closed case behave identically to before the fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
157 lines
6.7 KiB
JavaScript
157 lines
6.7 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* tdd-walk.qa.test.cjs — self-tests for `tests/qa/tdd-walk.cjs` (#4298, Phase 5
|
|
* of epic #4272).
|
|
*
|
|
* Matrix rows referenced below are from
|
|
* `.gsd/phase/chore-4298-tdd-walk-qa-harness/50-test-matrix.md`. Every test
|
|
* here runs the REAL shipped bash resolution against a REAL temp project —
|
|
* no reimplementation of the predicate, no text-shape assertions on the
|
|
* workflow markdown standing in for execution.
|
|
*/
|
|
|
|
const { describe, test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
const { TddWalk, DISPATCH_STEP_PATH, TDD_EMBED_TERNARY } = require('./qa/tdd-walk.cjs');
|
|
const { readFileNormalized } = require('./helpers.cjs');
|
|
|
|
const TDD_TYPE_PLAN = `---
|
|
type: tdd
|
|
---
|
|
<task type="auto"><name>a</name></task>
|
|
`;
|
|
|
|
const PLAIN_PLAN = `<task type="auto"><name>a</name></task>\n`;
|
|
|
|
describe('tdd-walk harness', () => {
|
|
test('row 1 — CLI predicate resolution: type: tdd resolves applicable/plan_frontmatter', (t) => {
|
|
const walk = TddWalk.create();
|
|
t.after(() => walk.cleanup());
|
|
walk.writePlan(TDD_TYPE_PLAN);
|
|
const result = walk.resolveViaCli();
|
|
assert.equal(result.success, true, result.error);
|
|
assert.equal(result.applicable, true);
|
|
assert.equal(result.source, 'plan_frontmatter');
|
|
});
|
|
|
|
test('row 1 — CLI predicate resolution: nothing set resolves not-applicable/none', (t) => {
|
|
const walk = TddWalk.create();
|
|
t.after(() => walk.cleanup());
|
|
walk.writePlan(PLAIN_PLAN);
|
|
const result = walk.resolveViaCli();
|
|
assert.equal(result.success, true, result.error);
|
|
assert.equal(result.applicable, false);
|
|
assert.equal(result.source, 'none');
|
|
});
|
|
|
|
test('row 2 — harness-backend script execution: type: tdd resolves TDD_APPLICABLE=true', (t) => {
|
|
const walk = TddWalk.create();
|
|
t.after(() => walk.cleanup());
|
|
walk.writePlan(TDD_TYPE_PLAN);
|
|
const result = walk.resolveViaBackend('harness');
|
|
assert.equal(result.success, true, result.stderr);
|
|
assert.equal(result.value, 'true');
|
|
});
|
|
|
|
test('row 2 — harness-backend script execution: plain plan resolves TDD_APPLICABLE=false', (t) => {
|
|
const walk = TddWalk.create();
|
|
t.after(() => walk.cleanup());
|
|
walk.writePlan(PLAIN_PLAN);
|
|
const result = walk.resolveViaBackend('harness');
|
|
assert.equal(result.success, true, result.stderr);
|
|
assert.equal(result.value, 'false');
|
|
});
|
|
|
|
test('row 3 — worktree-backend script execution: type: tdd resolves TDD_APPLICABLE=true', (t) => {
|
|
const walk = TddWalk.create();
|
|
t.after(() => walk.cleanup());
|
|
walk.writePlan(TDD_TYPE_PLAN);
|
|
const result = walk.resolveViaBackend('worktree');
|
|
assert.equal(result.success, true, result.stderr);
|
|
assert.equal(result.value, 'true');
|
|
});
|
|
|
|
test('row 3 — worktree-backend script execution: plain plan resolves TDD_APPLICABLE=false', (t) => {
|
|
const walk = TddWalk.create();
|
|
t.after(() => walk.cleanup());
|
|
walk.writePlan(PLAIN_PLAN);
|
|
const result = walk.resolveViaBackend('worktree');
|
|
assert.equal(result.success, true, result.stderr);
|
|
assert.equal(result.value, 'false');
|
|
});
|
|
|
|
test('row 4 — cross-backend agreement: same fixture plan, identical resolved value (#4264/#4265)', (t) => {
|
|
const tddWalk = TddWalk.create();
|
|
t.after(() => tddWalk.cleanup());
|
|
tddWalk.writePlan(TDD_TYPE_PLAN);
|
|
const harnessResult = tddWalk.resolveViaBackend('harness');
|
|
const worktreeResult = tddWalk.resolveViaBackend('worktree');
|
|
assert.equal(harnessResult.success, true, harnessResult.stderr);
|
|
assert.equal(worktreeResult.success, true, worktreeResult.stderr);
|
|
assert.equal(harnessResult.value, worktreeResult.value);
|
|
assert.equal(harnessResult.value, 'true');
|
|
|
|
const plainWalk = TddWalk.create();
|
|
t.after(() => plainWalk.cleanup());
|
|
plainWalk.writePlan(PLAIN_PLAN);
|
|
const harnessPlain = plainWalk.resolveViaBackend('harness');
|
|
const worktreePlain = plainWalk.resolveViaBackend('worktree');
|
|
assert.equal(harnessPlain.success, true, harnessPlain.stderr);
|
|
assert.equal(worktreePlain.success, true, worktreePlain.stderr);
|
|
assert.equal(harnessPlain.value, worktreePlain.value);
|
|
assert.equal(harnessPlain.value, 'false');
|
|
});
|
|
|
|
test('row 5 — fail-closed: missing plan file exits non-zero with the TDD-resolution FATAL on stderr (worktree backend)', (t) => {
|
|
const walk = TddWalk.create();
|
|
t.after(() => walk.cleanup());
|
|
// Deliberately no writePlan() call — {phase_dir}/{plan_file} points at a
|
|
// plan that does not exist on disk.
|
|
const result = walk.resolveViaBackend('worktree');
|
|
assert.equal(result.success, false);
|
|
// #4298 Standards+Spec review: a bare `.includes('FATAL')` would also
|
|
// pass if the file's OTHER fail-closed guard (the unrelated ISOLATION
|
|
// resolution, which shares the same first fenced block and can emit its
|
|
// own differently-worded FATAL) fired instead of the TDD-applicability
|
|
// one — silently proving the wrong guard. Assert the exact TDD-resolution
|
|
// FATAL text (from executor-isolation-dispatch.md's own echo line) so a
|
|
// future edit that changes which guard fires here is caught.
|
|
assert.ok(
|
|
result.stderr.includes("could not resolve TDD-applicability for plan"),
|
|
`expected stderr to contain the TDD-applicability FATAL message, got: ${result.stderr}`,
|
|
);
|
|
});
|
|
|
|
test('row 6 — #3800: the tdd.md embed ternary exists and its condition tracks the real predicate value', (t) => {
|
|
const dispatchContent = readFileNormalized(DISPATCH_STEP_PATH);
|
|
assert.ok(
|
|
dispatchContent.includes(TDD_EMBED_TERNARY),
|
|
'executor-isolation-dispatch.md no longer contains the documented TDD_APPLICABLE embed ternary',
|
|
);
|
|
|
|
const tddWalk = TddWalk.create();
|
|
t.after(() => tddWalk.cleanup());
|
|
tddWalk.writePlan(TDD_TYPE_PLAN);
|
|
const tddResult = tddWalk.resolveViaBackend('harness');
|
|
assert.equal(tddResult.success, true, tddResult.stderr);
|
|
// The embed ternary fires (includes tdd.md) exactly when TDD_APPLICABLE
|
|
// resolved to the literal string "true".
|
|
assert.equal(tddResult.value, 'true');
|
|
|
|
const plainWalk = TddWalk.create();
|
|
t.after(() => plainWalk.cleanup());
|
|
plainWalk.writePlan(PLAIN_PLAN);
|
|
const plainResult = plainWalk.resolveViaBackend('harness');
|
|
assert.equal(plainResult.success, true, plainResult.stderr);
|
|
assert.equal(plainResult.value, 'false');
|
|
});
|
|
});
|
|
|
|
// Non-regression (see 50-test-matrix.md "Non-regression"): this file shares
|
|
// no file with tests/tdd-single-statement.test.cjs, tests/tdd-backend-wiring.test.cjs,
|
|
// or tests/phase-tdd-applicable.test.cjs — it only reuses tests/helpers.cjs
|
|
// and tests/qa/tdd-walk.cjs (new, own to this phase), so those suites' own
|
|
// runs are the actual non-regression proof; nothing further to assert here.
|