* 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>
98 lines
3.5 KiB
JavaScript
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);
|
|
}),
|
|
);
|
|
});
|
|
});
|