Files
msd-core/tests/verify-archive-dirs-live-path.test.cjs
Tom Boucher 213a2fff63 chore(#3813): delete the caller-less listMilestoneArchiveDirs seam; #1883 contract now pins the live path (#4029)
* test(#3813): pin the #1883 unreadable-milestones contract on the live planning-snapshot path

* fix(#3813): delete the caller-less listMilestoneArchiveDirs seam; #1883 contract now pins the live path

* chore(#3813): changeset fragment (pr number backfilled after PR creation)

* chore(#3813): backfill changeset PR number (4029)

* chore(#3813): docs-exempt marker — internal dead-code removal

---------

Co-authored-by: sim <sim@local>
2026-08-29 07:44:22 -04: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('../gsd-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(), 'gsd-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(), 'gsd-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(), 'gsd-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');
});
});