/simplify over a97efb6..HEAD (the plan_version 1.9 / trial-4 delivery). - coordinator-contract: engine names come from engine-findings ENGINES; one markedMajorCodeReview predicate under both lowering checks; isEngineFinding everywhere; the unreachable branch (c) of illegalBriefLowering folded into (e). - armB workflow: RULE_KEYS = Object.keys(RULE_TIERS); the engine map is a literal pinned equal to ENGINES by the workflow test. - plan-schema: usesFilesGrammar() is the one 1.9 check (validator, sweep, testFirstFiles, stepTestsOnlyFromFields); opensEntry built on opensChunk. - plan-validator: one issue builder for the Files and Test first grammar. - plan-scope-sweep: the 1.9 and the 6.2.0 reader are one if/else. - artifact-commit: TRAILER_RE/trailerProblem no longer exported (no importer); the order comment says the CLI finds the repo root first. - tests: the lock drivers in session-id-isolation (d) run side by side per mode (~100 s -> ~30 s); a shared artifact-repo helper; severityRank and extractFindingsBlock reused; childEnv(env) without a spread; one copy of the PLAN_VERSION_CURRENT check. Plan readers: validator errors/warnings, plannedPaths, fieldPaths, testFirstFiles and stepTestsOnlyFromFields compared byte for byte against HEAD over 510 plans / 1692 steps plus 210 synthetic 1.7-1.9 cases: same. Suite 2384 (2382/0/2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
61 lines
3.2 KiB
JavaScript
61 lines
3.2 KiB
JavaScript
// tests/lib/files-reader-regressions.test.mjs
|
|
// 6.4 / regressions A, B and C of the `Files:` reader (fieldPaths in
|
|
// lib/parsers/plan-schema.mjs): three inputs 6.3.0 read right and 6.3.1 read
|
|
// wrong. Each case goes through stepTestsOnly on a 1.8-shaped step and through
|
|
// the validator's PLAN_STEP_TESTS_ONLY on a 1.8 plan. The controls are the
|
|
// round 7-10 neighbours of each case and the plain path right after a
|
|
// backticked one: they pin 6.3.1's answers and stay as they are.
|
|
|
|
import { test } from 'node:test';
|
|
import { strict as assert } from 'node:assert';
|
|
import { buildPlan } from '../helpers/plan-v18-builder.mjs';
|
|
import { validatePlanContent } from '../../lib/validators/plan-validator.mjs';
|
|
import { stepTestsOnly } from '../../lib/parsers/plan-schema.mjs';
|
|
|
|
const step = (files) => `- **Files:** ${files}\n- **Changes:** x\n`;
|
|
|
|
// A 1.8 plan whose step 2 carries `files` as its `Files:` field.
|
|
const planWith = (files) => buildPlan({ steps: [{}, { file: 'src/mod2.mjs', filesRaw: files, endToEnd: 'bash tests/e2e.sh' }] });
|
|
|
|
const testsOnlyWarned = (files) => validatePlanContent(planWith(files)).warnings
|
|
.some((w) => w.code === 'PLAN_STEP_TESTS_ONLY' && /Step 2\b/.test(w.message));
|
|
|
|
// [case, Files: text, 6.3.0's paths]
|
|
const CASES = [
|
|
['A: a comma chunk that opens with a plain file ends the description',
|
|
'`tests/a.test.mjs` - new test, src/a.mjs - the fix', ['tests/a.test.mjs', 'src/a.mjs']],
|
|
['B: a plain slash path inside a description, with no comma, is no file',
|
|
'`tests/a.test.mjs` covers the parser in src/parse.mjs', ['tests/a.test.mjs']],
|
|
['C: a backticked bare name alone on a new line is a file',
|
|
'`tests/a.test.mjs` - new test\n `LICENSE`', ['tests/a.test.mjs', 'LICENSE']],
|
|
];
|
|
|
|
for (const [name, files, paths] of CASES) {
|
|
test(`6.4 / regression ${name}`, () => {
|
|
const testsOnly = paths.every((p) => p.startsWith('tests/'));
|
|
assert.deepEqual(stepTestsOnly(step(files)), { testsOnly, files: paths }, files);
|
|
assert.equal(testsOnlyWarned(files), testsOnly,
|
|
`PLAN_STEP_TESTS_ONLY ${testsOnly ? 'fires' : 'stays quiet'} on a 1.8 plan: ${files}`);
|
|
});
|
|
}
|
|
|
|
// [control, Files: text, 6.3.1's paths]
|
|
const CONTROLS = [
|
|
['r7 (comma form of B): a plain slash path in its own comma entry is read',
|
|
'`tests/x.test.mjs`, the CLI entry src/cli.mjs', ['tests/x.test.mjs', 'src/cli.mjs']],
|
|
['r9 (A\'s neighbour): a description runs on past a comma',
|
|
'`tests/a.test.mjs` — mocks fs, child_process.spawnSync and path.join', ['tests/a.test.mjs']],
|
|
['r10 (C\'s neighbour): a backticked capitalised name inside a description is no file',
|
|
'`tests/a.test.mjs` — covers `parse`, `Parser`, `ParseError`', ['tests/a.test.mjs']],
|
|
['B\'s neighbour: a plain path right after the backticked one is read',
|
|
'`tests/a.test.mjs` src/b.mjs', ['tests/a.test.mjs', 'src/b.mjs']],
|
|
];
|
|
|
|
for (const [name, files, paths] of CONTROLS) {
|
|
test(`6.4 / control ${name}`, () => {
|
|
const testsOnly = paths.every((p) => p.startsWith('tests/'));
|
|
assert.deepEqual(stepTestsOnly(step(files)), { testsOnly, files: paths }, files);
|
|
assert.equal(testsOnlyWarned(files), testsOnly,
|
|
`PLAN_STEP_TESTS_ONLY ${testsOnly ? 'fires' : 'stays quiet'} on a 1.8 plan: ${files}`);
|
|
});
|
|
}
|