Files
msd-core/tests/verify-archive-dirs-live-path.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

76 lines
3.5 KiB
JavaScript

'use strict';
// ─────────────────────────────────────────────────────────────────────────────
// #1883 regression coverage on the LIVE path (#3813 re-point).
//
// The original #1883 suite asserted EACCES/EIO propagation on
// verify.cts's listMilestoneArchiveDirs — which lost its last production
// caller in Phase 12 (#3310) and survived only as a test seam, so the
// suite guarded dead code while the LIVE archived-phase enumeration
// (planning-snapshot.cts's buildArchivedPhaseTokensField) answered the
// same contract with a different, deliberate shape: a non-ENOENT
// milestones/ read failure yields scope UNREADABLE — never a silent
// COMPLETE "no archives". This suite pins the live shape.
// ─────────────────────────────────────────────────────────────────────────────
const { test, describe } = 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 { buildPlanningSnapshot } = require('../msd-core/bin/lib/planning-snapshot.cjs');
function fsError(code, targetPath) {
const err = new Error(`${code}: operation failed. scandir '${targetPath}'`);
err.code = code;
err.syscall = 'scandir';
err.path = targetPath;
return err;
}
// Inject a readdirSync fault scoped to the milestones/ path under test.
// t.mock auto-restores after each test — no chmod 0o000 (root bypasses mode bits).
function injectMilestonesFault(t, code, targetPath) {
const originalReaddirSync = fs.readdirSync;
t.mock.method(fs, 'readdirSync', function (p, ...rest) {
if (typeof p === 'string' && p.endsWith(path.join('milestones'))) {
throw fsError(code, targetPath);
}
return originalReaddirSync.call(this, p, ...rest);
});
}
function scopeOf(snapshot) {
const field = snapshot && snapshot.archivedPhaseTokens;
return field ? field.scope : undefined;
}
describe('#1883 (live path, #3813): an unreadable milestones/ dir is UNREADABLE, never silent emptiness', () => {
test('a permission (EACCES) failure yields scope UNREADABLE, not COMPLETE', (t) => {
const planBase = path.join(os.tmpdir(), 'msd-1883live-eacces-' + process.pid);
injectMilestonesFault(t, 'EACCES', path.join(planBase, 'milestones'));
const snap = buildPlanningSnapshot(planBase);
assert.equal(
scopeOf(snap),
'unreadable',
'#1883/#3813: an unreadable milestones/ dir must never read as genuine absence (COMPLETE)',
);
});
test('any non-ENOENT failure (EIO) yields scope UNREADABLE', (t) => {
const planBase = path.join(os.tmpdir(), 'msd-1883live-eio-' + process.pid);
injectMilestonesFault(t, 'EIO', path.join(planBase, 'milestones'));
const snap = buildPlanningSnapshot(planBase);
assert.equal(scopeOf(snap), 'unreadable', 'every non-ENOENT error must flag UNREADABLE');
});
test('an absent milestones/ dir (ENOENT) is a genuine empty: COMPLETE + []', () => {
const planBase = path.join(os.tmpdir(), 'msd-1883live-absent-' + process.pid);
const snap = buildPlanningSnapshot(planBase);
const field = snap.archivedPhaseTokens;
assert.equal(field.scope, 'complete', 'genuine absence stays COMPLETE');
assert.deepEqual(field.value, [], 'genuine absence yields no archived tokens');
});
});