test(#3339): fold the state/phase/dispatch & model-profile issue-* cluster — Wave 7
Folds 9 legacy issue-*.test.cjs regression files (140 test() blocks) into their module's main suite, per H3 (#3315) of the test-hygiene epic (#3053). LAST of 4 issue-* waves — closes out the 74-file fix-*/issue-* backlog (pending BUG_FILE_RE extension, held for a follow-up commit until Wave 6 is confirmed merged, per the epic's own zero-backlog precondition). - issue-2828-flat-roadmap-total-phases.test.cjs (1) + issue-3204-state- writer-phase-count.test.cjs (21): both target state-document.cjs buildStateFrontmatter via different CLI entrypoints — merged jointly into state-document.test.cjs, 0 dropped. - issue-2945-phase-complete-checkbox-rollback.test.cjs (4) + issue-2949- phase-complete-stage3-sentinel.test.cjs (4): both target phase.cts cmdPhaseComplete; issue explicitly warned of overlap — verified disjoint fixtures/assertions, 0 dropped, merged into phase.test.cjs. - issue-2927-reviewer-lane-overlay-invocation.test.cjs (10) merged into review-lane-descriptor.test.cjs. - issue-2939-dispatch-flatten-maxdepth.test.cjs (9) merged into host-integration.test.cjs, 2 dropped as verified exact duplicates. - issue-2977-frontmatter-bom.test.cjs (5) merged into frontmatter.test.cjs. - issue-2045-third-party-skills-surface.test.cjs (6) merged into capability-loader.test.cjs. - issue-2517-runtime-aware-profiles.test.cjs (80, the largest single fold in the epic) merged into model-resolver.test.cjs, 1 dropped as a verified true duplicate (checked against src/model-resolver.cts logic, not just title similarity). Fixed a genuine eslint irregular-whitespace finding: a literal BOM character embedded in a doc comment (pre-existing content from the original #2977 source, illustrating what a BOM looks like) — replaced with a readable U+FEFF notation. 3 stale doc references found and fixed (docs/adr/2313, 3180, 443). Zero net test-coverage loss. No production code changed.
This commit is contained in:
@@ -2243,3 +2243,89 @@ describe('#2847: schema name consistency between gsd-planner.md and src/frontmat
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────
|
||||
// Folded from tests/issue-2977-frontmatter-bom.test.cjs — test-hygiene sweep #3339 (H3 Wave 7)
|
||||
// ────────────────────────────────────────────────────────────────────────
|
||||
{
|
||||
const { describe: __foldDescribe } = require('node:test');
|
||||
__foldDescribe('folded:issue-2977-frontmatter-bom', () => {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Regression test for #2977 — `extractFrontmatter` returns {} for any file whose
|
||||
* frontmatter fence is preceded by a UTF-8 BOM (Windows PowerShell `>`/`Out-File`,
|
||||
* several editors). The `startsWith('---')` byte-0 check fails on any leading byte,
|
||||
* so every frontmatter field silently disappears with no error.
|
||||
*
|
||||
* The fix strips a leading UTF-8 BOM (U+FEFF) before the fence check. Scope: BOM only
|
||||
* (acceptance criteria 1-3). The generalized "arbitrary content before the fence" fork
|
||||
* (tolerate vs diagnose) is a product-intent decision, surfaced in the PR — out of scope.
|
||||
*
|
||||
* Matrix: .gsd/bug/fix/2977-frontmatter-bom-tolerance/50-test-matrix.md
|
||||
*/
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { extractFrontmatter } = require('../gsd-core/bin/lib/frontmatter.cjs');
|
||||
|
||||
const BOM = '\uFEFF';
|
||||
|
||||
describe('extractFrontmatter BOM tolerance (#2977)', () => {
|
||||
test('bomPrefixedFrontmatterParses', () => {
|
||||
// Row 1 (failing-first regression): a BOM-prefixed frontmatter document parses
|
||||
// identically to the same document without the BOM.
|
||||
const clean = '---\ntitle: T\nphase: "01"\nstatus: passed\n---\n\n# Body\n';
|
||||
const bommed = BOM + clean;
|
||||
const expected = extractFrontmatter(clean, 'a.md');
|
||||
const actual = extractFrontmatter(bommed, 'a.md');
|
||||
assert.deepEqual(actual, expected, 'BOM-prefixed frontmatter must parse identically to no-BOM');
|
||||
assert.strictEqual(actual.title, 'T', 'title field recovered');
|
||||
assert.strictEqual(actual.phase, '01', 'phase field recovered');
|
||||
assert.strictEqual(actual.status, 'passed', 'status field recovered');
|
||||
});
|
||||
|
||||
test('bomWithCrlfParses', () => {
|
||||
// Row 2 (acceptance #2): BOM + CRLF line endings together still parse correctly.
|
||||
const clean = '---\r\ntitle: T\r\nphase: "01"\r\n---\r\n\r\n# Body\r\n';
|
||||
const bommed = BOM + clean;
|
||||
const actual = extractFrontmatter(bommed, 'a.md');
|
||||
assert.strictEqual(actual.title, 'T', 'title recovered (BOM + CRLF)');
|
||||
assert.strictEqual(actual.phase, '01', 'phase recovered (BOM + CRLF)');
|
||||
});
|
||||
|
||||
test('bomWithNoFrontmatterStaysEmpty', () => {
|
||||
// Row 3 (acceptance #3): a BOM prefixing a document with no frontmatter (or genuinely
|
||||
// empty frontmatter) returns {} with no false diagnostic — same as no-BOM.
|
||||
assert.deepEqual(extractFrontmatter(BOM + 'just plain text', 'a.md'), {}, 'BOM + no frontmatter -> {}');
|
||||
assert.deepEqual(extractFrontmatter(BOM + '', 'a.md'), {}, 'BOM + empty -> {}');
|
||||
// A thematic-break-first-line Markdown doc (--- then prose) must stay {} — protected by
|
||||
// the existing false-positive threshold; the BOM strip must not lower that bar.
|
||||
assert.deepEqual(extractFrontmatter(BOM + '---\n\nA horizontal rule, not frontmatter.\n', 'a.md'), {},
|
||||
'BOM + thematic-break Markdown -> {} (no false diagnostic)');
|
||||
});
|
||||
|
||||
test('bomAcrossArtifactTypes', () => {
|
||||
// Row 4 (acceptance #1 across artifact types): each frontmatter-bearing artifact shape
|
||||
// recovers its fields when BOM-prefixed.
|
||||
const cases = [
|
||||
{ name: 'STATE.md', body: '---\ncurrent_phase: "01"\nstatus: "In progress"\n---\n\n# State\n', expect: { current_phase: '01', status: 'In progress' } },
|
||||
{ name: 'PLAN.md', body: '---\nphase: "01"\nplan: "01-01"\nstatus: "done"\n---\n\n# Plan\n', expect: { phase: '01', plan: '01-01', status: 'done' } },
|
||||
{ name: 'SUMMARY.md', body: '---\none-liner: "shipped the thing"\n---\n\n# Summary\n', expect: { 'one-liner': 'shipped the thing' } },
|
||||
{ name: 'UAT.md', body: '---\nphase: "02"\nverdict: "pass"\n---\n\n# UAT\n', expect: { phase: '02', verdict: 'pass' } },
|
||||
];
|
||||
for (const c of cases) {
|
||||
const actual = extractFrontmatter(BOM + c.body, c.name);
|
||||
assert.deepEqual(actual, c.expect, `${c.name}: BOM-prefixed frontmatter must recover fields`);
|
||||
}
|
||||
});
|
||||
|
||||
test('controlNoBom', () => {
|
||||
// Row 5 (no regression): no BOM, valid frontmatter still parses correctly (unchanged).
|
||||
const actual = extractFrontmatter('---\ntitle: T\nphase: "01"\n---\n\n# Body\n', 'a.md');
|
||||
assert.strictEqual(actual.title, 'T');
|
||||
assert.strictEqual(actual.phase, '01');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user