test(build): extend behavioral drift checks to all four pure functions
This commit is contained in:
@@ -71,3 +71,124 @@ test('bundle summary behavior matches src/pure.js (catches Math.floor-style regr
|
||||
// And specifically: perDay must be the floored integer 70, not the float 70.57.
|
||||
assert.equal(bundleResult.perDay, 70, 'perDay should be floored to 70');
|
||||
});
|
||||
|
||||
test('bundle parseTarget behavior matches src/pure.js', () => {
|
||||
const src = readFileSync(join(root, 'src/pure.js'), 'utf8');
|
||||
const bundle = readFileSync(join(root, 'torn-attribute-tracker.user.js'), 'utf8');
|
||||
|
||||
function extractBody(source, fnName) {
|
||||
const start = source.indexOf('function ' + fnName + '(');
|
||||
if (start === -1) throw new Error('could not find ' + fnName + ' in source');
|
||||
let i = source.indexOf('{', start);
|
||||
if (i === -1) throw new Error('no opening brace for ' + fnName);
|
||||
let depth = 1;
|
||||
i++;
|
||||
while (i < source.length && depth > 0) {
|
||||
const c = source[i];
|
||||
if (c === '{') depth++;
|
||||
else if (c === '}') depth--;
|
||||
i++;
|
||||
}
|
||||
return source.slice(start, i);
|
||||
}
|
||||
|
||||
// Both src and bundle reference module-scope `SUFFIXES`, so inject it for both.
|
||||
const srcFn = eval('(function() { const SUFFIXES = { k: 1e3, m: 1e6, b: 1e9, t: 1e12 }; ' + extractBody(src, 'parseTarget') + ' ; return parseTarget; })()');
|
||||
const bundleFn = eval('(function() { const SUFFIXES = { k: 1e3, m: 1e6, b: 1e9, t: 1e12 }; ' + extractBody(bundle, 'parseTarget') + ' ; return parseTarget; })()');
|
||||
|
||||
for (const input of [25, 25000000, '25M', '1.5B', '25,000,000', 'abc', null, undefined, 0, -1]) {
|
||||
assert.equal(bundleFn(input), srcFn(input), 'parseTarget drift on input: ' + JSON.stringify(input));
|
||||
}
|
||||
});
|
||||
|
||||
test('bundle computeEstimate behavior matches src/pure.js', () => {
|
||||
const src = readFileSync(join(root, 'src/pure.js'), 'utf8');
|
||||
const bundle = readFileSync(join(root, 'torn-attribute-tracker.user.js'), 'utf8');
|
||||
|
||||
function extractBody(source, fnName) {
|
||||
const start = source.indexOf('function ' + fnName + '(');
|
||||
if (start === -1) throw new Error('could not find ' + fnName + ' in source');
|
||||
let i = source.indexOf('{', start);
|
||||
if (i === -1) throw new Error('no opening brace for ' + fnName);
|
||||
let depth = 1;
|
||||
i++;
|
||||
while (i < source.length && depth > 0) {
|
||||
const c = source[i];
|
||||
if (c === '{') depth++;
|
||||
else if (c === '}') depth--;
|
||||
i++;
|
||||
}
|
||||
return source.slice(start, i);
|
||||
}
|
||||
|
||||
// The bundle inlines the 86_400_000 constant; src/pure.js uses MS_PER_DAY.
|
||||
// Both versions call `Date.now()` inside the function for the `eta` Date, so
|
||||
// we strip `eta` from the comparison (it would differ by sub-millisecond
|
||||
// jitter between the two evals) and only compare the numeric fields.
|
||||
const srcFn = eval('(function() { const MS_PER_DAY = 86_400_000; ' + extractBody(src, 'computeEstimate') + ' ; return computeEstimate; })()');
|
||||
const bundleFn = eval('(function() { ' + extractBody(bundle, 'computeEstimate') + ' ; return computeEstimate; })()');
|
||||
|
||||
function stripEta(r) {
|
||||
return { remaining: r.remaining, trainsToGo: r.trainsToGo, days: r.days, etaDays: r.days };
|
||||
}
|
||||
|
||||
for (const args of [
|
||||
[14_328_501, 25_000_000, 247, 4520],
|
||||
[25_000_000, 25_000_000, 247, 4520],
|
||||
[30_000_000, 25_000_000, 247, 4520],
|
||||
[100, 200, 0, 50],
|
||||
[100, 200, 50, 0],
|
||||
]) {
|
||||
const [c, t, pt, pd] = args;
|
||||
const srcResult = srcFn(c, t, pt, pd);
|
||||
const bundleResult = bundleFn(c, t, pt, pd);
|
||||
// Both eta fields should be null together, or both should be Date objects.
|
||||
assert.equal(bundleResult.eta === null, srcResult.eta === null, 'eta nullity drift on args: ' + JSON.stringify(args));
|
||||
assert.deepEqual(stripEta(bundleResult), stripEta(srcResult), 'computeEstimate drift on args: ' + JSON.stringify(args));
|
||||
}
|
||||
});
|
||||
|
||||
test('bundle pruneHistory behavior matches src/pure.js (catches strict-boundary regressions)', () => {
|
||||
const src = readFileSync(join(root, 'src/pure.js'), 'utf8');
|
||||
const bundle = readFileSync(join(root, 'torn-attribute-tracker.user.js'), 'utf8');
|
||||
|
||||
function extractBody(source, fnName) {
|
||||
const start = source.indexOf('function ' + fnName + '(');
|
||||
if (start === -1) throw new Error('could not find ' + fnName + ' in source');
|
||||
let i = source.indexOf('{', start);
|
||||
if (i === -1) throw new Error('no opening brace for ' + fnName);
|
||||
let depth = 1;
|
||||
i++;
|
||||
while (i < source.length && depth > 0) {
|
||||
const c = source[i];
|
||||
if (c === '{') depth++;
|
||||
else if (c === '}') depth--;
|
||||
i++;
|
||||
}
|
||||
return source.slice(start, i);
|
||||
}
|
||||
|
||||
// The bundle inlines 30 * 86_400_000; src uses THIRTY_DAYS_MS = 30 * MS_PER_DAY.
|
||||
// Both must be visible to the extracted function body.
|
||||
const srcFn = eval('(function() { const MS_PER_DAY = 86_400_000; const THIRTY_DAYS_MS = 30 * MS_PER_DAY; ' + extractBody(src, 'pruneHistory') + ' ; return pruneHistory; })()');
|
||||
const bundleFn = eval('(function() { const THIRTY_DAYS_MS = 30 * 86_400_000; ' + extractBody(bundle, 'pruneHistory') + ' ; return pruneHistory; })()');
|
||||
|
||||
const NOW = 1_700_000_000_000;
|
||||
const DAY = 86_400_000;
|
||||
|
||||
// Critical case: entry at exactly 30 days. Source drops it (strict >), bundle should too.
|
||||
const boundaryEntries = [
|
||||
{ ts: NOW, delta: 1 },
|
||||
{ ts: NOW - 1 * DAY, delta: 1 },
|
||||
{ ts: NOW - 29 * DAY, delta: 1 },
|
||||
{ ts: NOW - 30 * DAY, delta: 1 }, // exactly 30 days — should be DROPPED
|
||||
{ ts: NOW - 31 * DAY, delta: 1 },
|
||||
];
|
||||
|
||||
const srcResult = srcFn(boundaryEntries, NOW);
|
||||
const bundleResult = bundleFn(boundaryEntries, NOW);
|
||||
|
||||
assert.deepEqual(bundleResult, srcResult, 'pruneHistory drift on 30-day boundary');
|
||||
assert.equal(srcResult.length, 3, 'source should keep 3 entries (drop the 30-day one)');
|
||||
assert.equal(bundleResult.length, 3, 'bundle should keep 3 entries (drop the 30-day one)');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user