Files
msd-core/tests/context-composer.property.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

230 lines
8.9 KiB
JavaScript

'use strict';
/**
* Property-based tests for context-composer.cjs
*
* Module: msd-core/bin/lib/context-composer.cjs
* Exported: composeWithinBudget({ fragments, budget, measure, options })
*
* Key invariants (issue #2929 criterion 4):
* - moreBudgetNeverYieldsLessContent: raising the budget never shrinks the
* total surviving content.
* - neverDroppableFragmentsAlwaysSurvive: `verbatim` and `isolate`
* fragments retain their exact original content in every non-hard-fail
* result.
* - flooredFragmentNeverFallsBelowItsFloor: a `flexReserve`d fragment's
* final measured size never drops below min(flexReserve, original).
* - omittedIsAlwaysASubsequenceOfDeclaredDroppableOrder: `metadata.omitted`
* never reorders the declared `drop`-strategy ids.
* - isolatePrefixIsByteStable: `metadata.isolatePrefix` is identical
* regardless of budget or measure function.
*/
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fc = require('./helpers/fast-check-setup.cjs');
const { composeWithinBudget } = require('../msd-core/bin/lib/context-composer.cjs');
// ─── Helpers ─────────────────────────────────────────────────────────────────
// Monotone measure functions: growing text never yields a smaller measure.
const measureChars4 = (text) => (text ? Math.ceil(text.length / 4) : 0);
const measureBytes = (text) => (text ? Buffer.byteLength(text, 'utf8') : 0);
const strategyArb = fc.oneof(
fc.constant({ kind: 'verbatim' }),
fc.record({ kind: fc.constant('head-shrink'), maxLines: fc.integer({ min: 0, max: 8 }) }),
fc.record({
kind: fc.constant('proportional-truncate'),
floorChars: fc.integer({ min: 0, max: 40 }),
}),
fc.constant({ kind: 'drop' })
);
/** A realistic mixed fragment set: varying content, all four strategies, unique ids. */
function fragmentsArb({ minLength = 0, maxLength = 8 } = {}) {
return fc
.array(
fc.record({
content: fc.string({ maxLength: 300 }),
strategy: strategyArb,
required: fc.boolean(),
flexReserve: fc.integer({ min: 0, max: 30 }),
}),
{ minLength, maxLength }
)
.map((items) =>
items.map((item, idx) => ({
id: `f${idx}`,
content: item.content,
strategy: item.strategy,
required: item.required,
flexReserve: item.flexReserve,
}))
);
}
/** Marks a random leading prefix of `fragments` as isolate (order-legal). */
function withIsolatePrefix(fragments, isolateCount) {
const n = Math.min(isolateCount, fragments.length);
return fragments.map((f, idx) => (idx < n ? { ...f, isolate: true } : f));
}
const totalContentLength = (result) => result.fragments.reduce((sum, f) => sum + f.content.length, 0);
// ─── Properties ──────────────────────────────────────────────────────────────
describe('context-composer: composeWithinBudget properties', () => {
test('property: moreBudgetNeverYieldsLessContent', () => {
fc.assert(
fc.property(
fragmentsArb(),
fc.integer({ min: 0, max: 2000 }),
fc.integer({ min: 0, max: 2000 }),
(fragments, budgetA, budgetB) => {
const lo = Math.min(budgetA, budgetB);
const hi = Math.max(budgetA, budgetB);
const resultLo = composeWithinBudget({ fragments, budget: lo, measure: measureChars4 });
const resultHi = composeWithinBudget({ fragments, budget: hi, measure: measureChars4 });
if (resultLo.metadata.hardFailed || resultHi.metadata.hardFailed) return;
const contentLo = totalContentLength(resultLo);
const contentHi = totalContentLength(resultHi);
assert.ok(
contentHi >= contentLo,
`budget ${hi} yielded less content (${contentHi}) than budget ${lo} (${contentLo})`
);
}
)
);
});
test('property: neverDroppableFragmentsAlwaysSurvive (verbatim + isolate)', () => {
fc.assert(
fc.property(
fragmentsArb({ minLength: 1 }),
fc.nat({ max: 8 }),
fc.integer({ min: 0, max: 50 }),
(fragments, isolateCount, budget) => {
const withIsolates = withIsolatePrefix(fragments, isolateCount);
const result = composeWithinBudget({ fragments: withIsolates, budget, measure: measureChars4 });
if (result.metadata.hardFailed) return;
for (const f of withIsolates) {
const isNeverDroppable = f.isolate === true || f.strategy.kind === 'verbatim';
if (!isNeverDroppable) continue;
const composed = result.fragments.find((c) => c.id === f.id);
assert.ok(composed, `fragment ${f.id} missing from result`);
assert.equal(
composed.content,
f.content,
`never-droppable fragment ${f.id} (isolate=${f.isolate === true}, strategy=${f.strategy.kind}) was altered`
);
}
}
)
);
});
test('property: flooredFragmentNeverFallsBelowItsFloor', () => {
fc.assert(
fc.property(fragmentsArb({ minLength: 1 }), fc.integer({ min: 0, max: 2000 }), (fragments, budget) => {
const result = composeWithinBudget({ fragments, budget, measure: measureChars4 });
if (result.metadata.hardFailed) return;
for (const f of fragments) {
if (!f.flexReserve || f.flexReserve <= 0) continue;
const composed = result.fragments.find((c) => c.id === f.id);
assert.ok(composed, `fragment ${f.id} missing from result`);
const originalMeasured = measureChars4(f.content);
const finalMeasured = measureChars4(composed.content);
const expectedFloor = Math.min(f.flexReserve, originalMeasured);
assert.ok(
finalMeasured >= expectedFloor,
`fragment ${f.id}: final measured size ${finalMeasured} < floor ${expectedFloor} ` +
`(flexReserve=${f.flexReserve}, originalMeasured=${originalMeasured})`
);
}
})
);
});
test('property: omittedIsAlwaysASubsequenceOfDeclaredDroppableOrder', () => {
fc.assert(
fc.property(fragmentsArb(), fc.integer({ min: 0, max: 2000 }), (fragments, budget) => {
const result = composeWithinBudget({ fragments, budget, measure: measureChars4 });
if (result.metadata.hardFailed) return;
const declaredDroppableOrder = fragments
.filter((f) => f.strategy.kind === 'drop')
.map((f) => f.id);
// A subsequence: walk declaredDroppableOrder once, consuming
// omitted ids in order — every omitted id must be found, in order,
// without skipping backward.
let cursor = 0;
for (const omittedId of result.metadata.omitted) {
const foundAt = declaredDroppableOrder.indexOf(omittedId, cursor);
assert.ok(
foundAt !== -1,
`omitted id "${omittedId}" is not a subsequence of declared droppable order ` +
`[${declaredDroppableOrder.join(', ')}] (omitted: [${result.metadata.omitted.join(', ')}])`
);
cursor = foundAt + 1;
}
})
);
});
test('property: isolatePrefixIsByteStable', () => {
fc.assert(
fc.property(
fragmentsArb({ minLength: 1 }),
fc.nat({ max: 8 }),
fc.integer({ min: 1, max: 5 }),
fc.integer({ min: 500, max: 5000 }),
(fragments, isolateCount, tinyBudget, hugeBudget) => {
const withIsolates = withIsolatePrefix(fragments, isolateCount);
if (!withIsolates.some((f) => f.isolate)) return;
const resultTinyChars = composeWithinBudget({
fragments: withIsolates,
budget: tinyBudget,
measure: measureChars4,
});
const resultHugeChars = composeWithinBudget({
fragments: withIsolates,
budget: hugeBudget,
measure: measureChars4,
});
const resultTinyBytes = composeWithinBudget({
fragments: withIsolates,
budget: tinyBudget,
measure: measureBytes,
});
const resultHugeBytes = composeWithinBudget({
fragments: withIsolates,
budget: hugeBudget,
measure: measureBytes,
});
const prefixes = [
resultTinyChars.metadata.isolatePrefix,
resultHugeChars.metadata.isolatePrefix,
resultTinyBytes.metadata.isolatePrefix,
resultHugeBytes.metadata.isolatePrefix,
];
for (const prefix of prefixes) {
assert.equal(
prefix,
prefixes[0],
`isolatePrefix diverged across budget/measure combinations: ${JSON.stringify(prefixes)}`
);
}
}
)
);
});
});