Files
msd-core/tests/gate-predicate-evaluator-missing.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

200 lines
6.4 KiB
JavaScript

const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const fc = require('fast-check');
const { evaluatePredicate } = require('../msd-core/bin/lib/gate-predicate-evaluator.cjs');
const { buildPredicateDeps } = require('../msd-core/bin/lib/check-command-router.cjs');
const dummyCtx = { cwd: '/fake/project' };
const basePredicate = {
kind: 'artifact-frontmatter-equals',
artifact: 'WINDOWS.md',
field: 'open_count',
equals: 0,
};
function dummyDeps(overrides = {}) {
return {
runBoundedShell: () => ({ exitCode: 0, stdout: '', stderr: '', signal: null, timedOut: false }),
readFrontmatter: () => ({ open_count: '0' }),
findPhaseArtifact: (dir, suffix) => path.join(dir, suffix),
...overrides,
};
}
test('evaluatePredicate handles artifact-frontmatter-equals kind with numeric string coercion', () => {
const deps = dummyDeps({
readFrontmatter: (filePath) => {
const base = path.basename(filePath);
if (base === 'clean-WINDOWS.md') {
return { open_count: '0' };
}
if (base === 'unclean-WINDOWS.md') {
return { open_count: '2' };
}
return {};
},
});
const cleanPred = {
kind: 'artifact-frontmatter-equals',
artifact: 'clean-WINDOWS.md',
field: 'open_count',
equals: 0,
};
const passRes = evaluatePredicate(cleanPred, dummyCtx, deps);
assert.strictEqual(passRes.block, false, 'pass on matching numeric frontmatter field even if read as string');
const uncleanPred = {
kind: 'artifact-frontmatter-equals',
artifact: 'unclean-WINDOWS.md',
field: 'open_count',
equals: 0,
};
const blockRes = evaluatePredicate(uncleanPred, dummyCtx, deps);
assert.strictEqual(blockRes.block, true, 'block on non-matching frontmatter field');
});
test('artifact-frontmatter-equals rejects malformed declarations', () => {
for (const artifact of [undefined, '']) {
assert.throws(
() => evaluatePredicate({ ...basePredicate, artifact }, dummyCtx, dummyDeps()),
/non-empty string "artifact"/,
);
}
for (const field of [undefined, '']) {
assert.throws(
() => evaluatePredicate({ ...basePredicate, field }, dummyCtx, dummyDeps()),
/non-empty string "field"/,
);
}
const { equals: _equals, ...withoutEquals } = basePredicate;
assert.throws(
() => evaluatePredicate(withoutEquals, dummyCtx, dummyDeps()),
/requires an "equals" key/,
);
});
test('artifact-frontmatter-equals fails closed for missing artifacts and fields', () => {
const missingArtifact = evaluatePredicate(
basePredicate,
dummyCtx,
dummyDeps({ findPhaseArtifact: () => null }),
);
assert.equal(missingArtifact.block, true);
assert.equal(missingArtifact.details.artifactNotFound, true);
const missingField = evaluatePredicate(
basePredicate,
dummyCtx,
dummyDeps({ readFrontmatter: () => ({}) }),
);
assert.equal(missingField.block, true);
assert.equal(missingField.details.match, false);
assert.equal(missingField.details.actual, undefined);
});
test('artifact-frontmatter-equals blocks non-numeric values and surfaces parse failures', () => {
const nonNumeric = evaluatePredicate(
basePredicate,
dummyCtx,
dummyDeps({ readFrontmatter: () => ({ open_count: 'clean' }) }),
);
assert.equal(nonNumeric.block, true);
assert.equal(nonNumeric.details.actual, 'clean');
assert.throws(
() => evaluatePredicate(
basePredicate,
dummyCtx,
dummyDeps({ readFrontmatter: () => { throw new Error('invalid frontmatter'); } }),
),
/invalid frontmatter/,
);
});
test('artifact-frontmatter-equals validates its kind-specific dependencies', () => {
const deps = dummyDeps();
delete deps.findPhaseArtifact;
assert.throws(
() => evaluatePredicate(basePredicate, dummyCtx, deps),
/predicate deps require a "findPhaseArtifact" function/,
);
const depsWithoutReader = dummyDeps();
delete depsWithoutReader.readFrontmatter;
assert.throws(
() => evaluatePredicate(basePredicate, dummyCtx, depsWithoutReader),
/predicate deps require a "readFrontmatter" function/,
);
});
test('property: stringified scalar frontmatter equals its JSON scalar declaration', () => {
const scalar = fc.oneof(
fc.integer(),
fc.boolean(),
fc.string({ maxLength: 40 }),
fc.constant(null),
);
fc.assert(
fc.property(scalar, (value) => {
const result = evaluatePredicate(
{ ...basePredicate, equals: value },
dummyCtx,
dummyDeps({ readFrontmatter: () => ({ open_count: String(value) }) }),
);
assert.equal(result.block, false);
}),
);
});
test('evaluatePredicate with real buildPredicateDeps resolves project-level .planning/WINDOWS.md', () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-test-pred-'));
try {
const planningDir = path.join(tmpDir, '.planning');
fs.mkdirSync(planningDir, { recursive: true });
const windowsPath = path.join(planningDir, 'WINDOWS.md');
fs.writeFileSync(windowsPath, '---\nopen_count: 0\n---\n# Windows\n', 'utf8');
const pred = {
kind: 'artifact-frontmatter-equals',
artifact: 'WINDOWS.md',
field: 'open_count',
equals: 0,
};
const ctx = { cwd: tmpDir };
const deps = buildPredicateDeps();
const res = evaluatePredicate(pred, ctx, deps);
assert.strictEqual(res.block, false, 'real buildPredicateDeps must find .planning/WINDOWS.md and pass on open_count: 0');
const parentDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-test-pred-parent-'));
try {
const projectDir = path.join(parentDir, 'project');
fs.mkdirSync(projectDir);
fs.writeFileSync(path.join(parentDir, 'secret.md'), '---\nopen_count: 0\n---\n', 'utf8');
assert.equal(deps.findPhaseArtifact(projectDir, '../secret.md'), null);
assert.equal(deps.findPhaseArtifact(projectDir, '..\\secret.md'), null);
} finally {
const { cleanup } = require('./helpers.cjs');
cleanup(parentDir);
}
} finally {
const { cleanup } = require('./helpers.cjs');
cleanup(tmpDir);
}
});
test('buildPredicateDeps reports an artifact removed before its frontmatter read', () => {
const missingPath = path.join(os.tmpdir(), 'msd-test-predicate-artifact-missing.md');
assert.throws(
() => buildPredicateDeps().readFrontmatter(missingPath),
/predicate artifact disappeared before it could be read/,
);
});