Files
msd-core/tests/context-index-sync.test.cjs
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

154 lines
6.4 KiB
JavaScript

'use strict';
/**
* Regression test for #2944 — 85140eac8 "refresh the predicate index staled
* by a merge race on next" (docs/CONTEXT-INDEX.json) shipped with zero
* behavioral test coverage.
*
* docs/CONTEXT-INDEX.json is a committed generated artifact guarded by
* `scripts/gen-context-index.cjs --check` in `lint:generated-sync`. A
* concurrent-merge race landed one PR's CONTEXT.md alongside another PR's
* index; each PR was green alone (lint only runs on the PR's own diff), the
* combination was red, and it only surfaced on the NEXT PR to run `lint:ci`.
* This file puts the same drift check inside the test suite (which
* `msd-test` runs on every PR), so a future racing merge fails here directly
* instead of waiting for a downstream PR to trip over `lint:generated-sync`.
*
* The example/production parser parity guard (the OTHER defect #2944
* introduced regression coverage for) lives in
* scripts/lint-example-parser-parity.cjs, wired into `npm run lint:ci` — NOT
* here. ADR-1671 ("Dynamic context management platform",
* docs/adr/1671-dynamic-context-management-platform.md:102) places
* examples/dynamic-context-management/ deliberately outside four surfaces:
* the build (src/ -> bin/lib/), the npm package files[], the installer, and
* the CI test suite (tests/) — this file. A `require()` of the example from
* inside tests/ would violate that fourth exclusion directly; a lint script
* that only reads both modules from a repo-root script does not.
*/
process.env.MSD_TEST_MODE = '1';
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const helpers = require('./helpers.cjs');
const REPO_ROOT = path.resolve(__dirname, '..');
const LIB_DIR = path.join(REPO_ROOT, 'msd-core', 'bin', 'lib');
const PROD_PREDICATES_PATH = path.join(LIB_DIR, 'context-predicates.cjs');
const CONTEXT_PATH = path.join(REPO_ROOT, 'CONTEXT.md');
const INDEX_PATH = path.join(REPO_ROOT, 'docs', 'CONTEXT-INDEX.json');
const prodPredicates = require(PROD_PREDICATES_PATH);
function readContextMarkdown() {
return fs.readFileSync(CONTEXT_PATH, 'utf8');
}
function readCommittedIndex() {
return JSON.parse(fs.readFileSync(INDEX_PATH, 'utf8'));
}
/**
* Diff two ContextIndex-shaped `{predicates:[{id,klass,value}]}` objects by
* id, returning human-readable divergence strings naming the exact
* predicate id(s) involved — so a failure reads as an actionable list, never
* "objects differ".
*/
function diffPredicatesById(leftPredicates, rightPredicates, leftLabel, rightLabel) {
const leftMap = new Map(leftPredicates.map((p) => [p.id, p]));
const rightMap = new Map(rightPredicates.map((p) => [p.id, p]));
const allIds = new Set([...leftMap.keys(), ...rightMap.keys()]);
const diffs = [];
for (const id of Array.from(allIds).sort()) {
const l = leftMap.get(id);
const r = rightMap.get(id);
if (l && !r) {
diffs.push(`${id}: present in ${leftLabel} but absent from ${rightLabel}`);
} else if (!l && r) {
diffs.push(`${id}: present in ${rightLabel} but absent from ${leftLabel}`);
} else if (l.value !== r.value) {
diffs.push(
`${id}: value diverged (${leftLabel}=${JSON.stringify(l.value)}, ${rightLabel}=${JSON.stringify(r.value)})`,
);
} else if (l.klass !== r.klass) {
diffs.push(
`${id}: klass diverged (${leftLabel}=${JSON.stringify(l.klass)}, ${rightLabel}=${JSON.stringify(r.klass)})`,
);
}
}
return diffs;
}
// ─── docs/CONTEXT-INDEX.json vs. a fresh CONTEXT.md parse ────────────────────
describe('docs/CONTEXT-INDEX.json sync with CONTEXT.md (#2944 concurrent-merge regression)', () => {
test('committed index matches a fresh parse of the real CONTEXT.md, predicate-by-predicate', () => {
const { parsePredicates, buildIndex } = prodPredicates;
const markdown = readContextMarkdown();
const { predicates } = parsePredicates(markdown);
const fresh = buildIndex(predicates);
const committed = readCommittedIndex();
const diffs = diffPredicatesById(
committed.predicates,
fresh.predicates,
'committed docs/CONTEXT-INDEX.json',
'fresh CONTEXT.md parse',
);
assert.deepEqual(
diffs,
[],
'docs/CONTEXT-INDEX.json has drifted from CONTEXT.md for predicate id(s) ' +
'(run `node scripts/gen-context-index.cjs --write` to refresh):\n' +
diffs.join('\n'),
);
assert.equal(
committed.count,
fresh.count,
`predicate count diverged: committed=${committed.count} fresh=${fresh.count}`,
);
assert.deepEqual(
committed.classes,
fresh.classes,
'class-count map diverged between committed index and fresh CONTEXT.md parse:\n' +
`committed=${JSON.stringify(committed.classes)}\nfresh=${JSON.stringify(fresh.classes)}`,
);
});
test('divergence detector is not vacuous: catches a mutated index in a throwaway temp copy (never the real artifact)', (t) => {
const { parsePredicates, buildIndex } = prodPredicates;
const markdown = readContextMarkdown();
const { predicates } = parsePredicates(markdown);
const fresh = buildIndex(predicates);
// Mutate a COPY of the fresh index in a fresh temp dir. docs/CONTEXT-
// INDEX.json itself is never opened for write anywhere in this file.
const mutated = JSON.parse(JSON.stringify(fresh));
const target =
mutated.predicates.find((p) => p.id === 'RULESET.WORKFLOW_SIZE_BUDGET') || mutated.predicates[0];
target.value = target.value + ' MUTATED-FOR-TEST';
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'context-index-sync-'));
t.after(() => helpers.cleanup(tmpDir));
const tmpIndexPath = path.join(tmpDir, 'CONTEXT-INDEX.json');
fs.writeFileSync(tmpIndexPath, JSON.stringify(mutated, null, 2) + '\n', 'utf8');
const mutatedCommitted = JSON.parse(fs.readFileSync(tmpIndexPath, 'utf8'));
const diffs = diffPredicatesById(
mutatedCommitted.predicates,
fresh.predicates,
'mutated temp-copy index',
'fresh CONTEXT.md parse',
);
assert.ok(diffs.length > 0, 'expected the mutated temp copy to diverge from the fresh parse');
assert.ok(
diffs.some((d) => d.startsWith(`${target.id}:`)),
`expected the diff to name ${target.id}; got:\n${diffs.join('\n')}`,
);
});
});