test(changelog): find the 6.4.0 section by its title, before and after the release commit
Two docs tests located this delivery's CHANGELOG section by the literal heading `## Unreleased` (one also required it to be the first section). The release commit renames it to `## v6.4.0 — <date> — <title>` (docs/release-runbook.md § 1.4), so all 16 content pins failed on the release commit while the content was the same. One shared helper, tests/helpers/changelog-section.mjs, now finds the section by its title under either heading, wherever it stands (a later delivery's section above it does not move the pins), with a trailing \r or spaces tolerated, and only when it stands exactly once. Every content assertion is unchanged; their messages no longer say "Unreleased". New tests pin the accepted headings and seven near misses (version, date, title, the literal dot in 1.9), and the helper on a later section, a \r, a duplicate and a missing section. Measured: 46/46 pass against main's CHANGELOG and against the release commit's CHANGELOG; before the change, 16 failed on the release commit. Three mutants of the helper (a looser date, no escaping, no trim) are each failed by one test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4e28dc5079
commit
b79495847d
3 changed files with 86 additions and 51 deletions
|
|
@ -12,6 +12,7 @@ import { readFileSync } from 'node:fs';
|
|||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { RULE_A, RULE_B, DIFF_FIELDS } from '../../lib/review/critic-diff.mjs';
|
||||
import { deliverySection } from '../helpers/changelog-section.mjs';
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
||||
const read = rel => readFileSync(join(ROOT, rel), 'utf-8');
|
||||
|
|
@ -68,29 +69,22 @@ test('Phase 9 no longer says the loop ends on a round with no BLOCKER', () => {
|
|||
});
|
||||
|
||||
// The narrow round of the A/B review (operator decisions 48 and 50): the
|
||||
// CHANGELOG's open Unreleased section records it, Phase 9 says what a merge
|
||||
// keeps, and Handover 4 states the matcher's overlap rule and the distinct id.
|
||||
// The release check is imported as a module namespace, so a missing parseDoors
|
||||
// export fails an assertion, not the load.
|
||||
// CHANGELOG's delivery section records it, Phase 9 says what a merge keeps, and
|
||||
// Handover 4 states the matcher's overlap rule and the distinct id. The section
|
||||
// is found by its title, before and after the release commit
|
||||
// (tests/helpers/changelog-section.mjs). The release check is imported as a
|
||||
// module namespace, so a missing parseDoors export fails an assertion, not the
|
||||
// load.
|
||||
const releaseCheck = await import('../../scripts/release-check.mjs');
|
||||
|
||||
// The body of the open `## Unreleased` section, up to the next `## ` heading.
|
||||
function unreleasedBody() {
|
||||
const lines = read('CHANGELOG.md').split('\n');
|
||||
const start = lines.findIndex(l => /^## Unreleased\b/.test(l));
|
||||
assert.ok(start >= 0, 'CHANGELOG.md has a "## Unreleased" section');
|
||||
let end = lines.length;
|
||||
for (let i = start + 1; i < lines.length; i++) {
|
||||
if (/^## /.test(lines[i])) { end = i; break; }
|
||||
}
|
||||
return lines.slice(start + 1, end).join('\n');
|
||||
}
|
||||
// The delivery section's body, up to the next `## ` heading.
|
||||
const unreleasedBody = () => deliverySection(read('CHANGELOG.md')).body;
|
||||
|
||||
// A `### <name>` body of the Unreleased section, up to the next heading.
|
||||
// A `### <name>` body of the delivery section, up to the next heading.
|
||||
function subsection(body, name) {
|
||||
const lines = body.split('\n');
|
||||
const start = lines.findIndex(l => l.replace(/\s+$/, '') === `### ${name}`);
|
||||
assert.ok(start >= 0, `the Unreleased section has a "### ${name}" heading`);
|
||||
assert.ok(start >= 0, `the delivery section has a "### ${name}" heading`);
|
||||
let end = lines.length;
|
||||
for (let i = start + 1; i < lines.length; i++) {
|
||||
if (/^###? /.test(lines[i])) { end = i; break; }
|
||||
|
|
@ -113,7 +107,7 @@ test('CHANGELOG: the narrow round of operator decisions 48 and 50', () => {
|
|||
assert.equal(typeof releaseCheck.parseDoors, 'function', 'scripts/release-check.mjs exports parseDoors');
|
||||
const body = unreleasedBody();
|
||||
const d = releaseCheck.parseDoors(body);
|
||||
assert.equal(d.found, true, 'the Unreleased section has a "### Doors" heading');
|
||||
assert.equal(d.found, true, 'the delivery section has a "### Doors" heading');
|
||||
assert.deepEqual(d.bad, [], 'every top-level door line reads "- one-way: ..." or "- two-way: ..."');
|
||||
const doors48 = d.entries.filter(e => /operator\s+decision\s+48\b/.test(flat(e.text)));
|
||||
assert.ok(doors48.length >= 3,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue