Files
msd-core/tests/write-set.test.cjs
Tom Boucher 2cbf186420 chore(#2143): fail-loud Result + per-surface write-set contract — Phase 3 (#2251)
* chore(#2143): fail-loud Result + per-surface write-set contract — Phase 3

Phase 3 of epic #2143 (ADR-2143 §5/§6). The three target bugs (#2140, #2112,
#2118) were already fixed tactically on next; this introduces the reusable
structural contracts and rewires the primary #2140 site onto them.

- src/write-set.cts (new): the parse `Result<T> = {ok,value|reason}` (§5) and the
  per-surface write-set (`WriteOutcome {surface, applied, requirement?}`,
  `WriteSet`, `writeSetComplete`) (§6). markdown-table.cts now imports + re-exports
  `Result` from here (single source; distinct from command-routing-hub's Result).
- requirements mark-complete (src/milestone.cts): returns a PER-REQUIREMENT,
  per-surface write-set; `write_set_complete` is true only if every surface of
  every requirement applied — structurally forbidding the #2140 OR-into-one-flag
  masking, including across a multi-ID batch (adversarial-review regression).
  Pre-existing output fields unchanged (behaviour-preserving; #2140 already fixed).
- deriveProgressFromRoadmap (src/phase-lifecycle.cts): removed the vestigial
  null-swallowing try/catch (findTableWithColumns never throws) — ADR §5 no-swallow;
  RoadmapProgress return contract unchanged.
- commit --files (#2112) and milestone complete --dry-run (#2118) left as-is
  (single-surface commit / pre-mutation preview — not genuine multi-surface writes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#2244): backfill changeset PR number (#2251)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:39:58 -04:00

98 lines
3.5 KiB
JavaScript

'use strict';
/**
* Behavioral tests for write-set.cjs
*
* Module: gsd-core/bin/lib/write-set.cjs
* Exports: writeSetComplete (Result<T> is a pure type re-exported at compile
* time only — nothing to assert on it at runtime beyond markdown-table.cjs's
* own re-export continuing to work, covered by tests/markdown-table.test.cjs).
*
* Covers (ADR-2143 §5/§6):
* - writeSetComplete happy path: all-applied → true, one-not-applied → false
* - BOUNDARY coverage: empty write-set (0 outcomes), single outcome, two outcomes
* - an empty write-set is never "complete" (no vacuous-true on a no-op)
* - fast-check property: writeSetComplete(ws) === (ws.length > 0 && every applied)
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fc = require('./helpers/fast-check-setup.cjs');
const { writeSetComplete } = require('../gsd-core/bin/lib/write-set.cjs');
describe('writeSetComplete', () => {
test('every surface applied → true', () => {
assert.equal(
writeSetComplete([
{ surface: 'checkbox', applied: true },
{ surface: 'traceability', applied: true },
]),
true,
);
});
test('one surface not applied → false (AND across surfaces, never OR)', () => {
assert.equal(
writeSetComplete([
{ surface: 'checkbox', applied: true },
{ surface: 'traceability', applied: false },
]),
false,
);
});
test('no surfaces applied → false', () => {
assert.equal(
writeSetComplete([
{ surface: 'checkbox', applied: false },
{ surface: 'traceability', applied: false },
]),
false,
);
});
// BOUNDARY: 0 outcomes (limit-1 relative to the smallest real set), 1 outcome
// (limit), 2 outcomes (limit+1) — the write-set shape has no upper bound, but
// the meaningful boundary here is "is there anything to be complete about".
test('BOUNDARY: empty write-set (0 outcomes) is never complete', () => {
assert.equal(writeSetComplete([]), false,
'an empty write-set must not vacuously report complete — that would let a ' +
'no-op masquerade as full success, the exact OR-into-one-flag class ADR-2143 §6 prohibits');
});
test('BOUNDARY: single-outcome write-set (1 outcome) — applied true is complete', () => {
assert.equal(writeSetComplete([{ surface: 'checkbox', applied: true }]), true);
});
test('BOUNDARY: single-outcome write-set (1 outcome) — applied false is not complete', () => {
assert.equal(writeSetComplete([{ surface: 'checkbox', applied: false }]), false);
});
test('BOUNDARY: two-outcome write-set (2 outcomes) — both true is complete', () => {
assert.equal(
writeSetComplete([
{ surface: 'checkbox', applied: true },
{ surface: 'traceability', applied: true },
]),
true,
);
});
// ─── Property test ───────────────────────────────────────────────────────────
const outcomeArb = fc.record({
surface: fc.string({ minLength: 1, maxLength: 12 }),
applied: fc.boolean(),
});
test('property: writeSetComplete(ws) === (ws.length > 0 && every outcome applied)', () => {
fc.assert(
fc.property(fc.array(outcomeArb, { maxLength: 8 }), (ws) => {
const expected = ws.length > 0 && ws.every((o) => o.applied);
assert.equal(writeSetComplete(ws), expected);
}),
);
});
});