feat(pure): computeEstimate with safe division and target-reached handling

This commit is contained in:
dev
2026-06-01 15:30:07 -05:00
parent 545cf65beb
commit faae7702aa
2 changed files with 49 additions and 1 deletions
+13
View File
@@ -23,3 +23,16 @@ export function parseTarget(input) {
const multiplier = suffix ? SUFFIXES[suffix] : 1; const multiplier = suffix ? SUFFIXES[suffix] : 1;
return Math.floor(num * multiplier); return Math.floor(num * multiplier);
} }
export function computeEstimate(current, target, perTrain, perDay) {
const remaining = Math.max(0, target - current);
if (remaining === 0) {
return { remaining: 0, trainsToGo: 0, days: 0, eta: null };
}
const trainsToGo = perTrain > 0 ? Math.ceil(remaining / perTrain) : 0;
const days = perDay > 0 ? Math.ceil(remaining / perDay) : 0;
const eta = days > 0 ? new Date(Date.now() + days * 86_400_000) : null;
return { remaining, trainsToGo, days, eta };
}
+36 -1
View File
@@ -1,6 +1,6 @@
import { test } from 'node:test'; import { test } from 'node:test';
import assert from 'node:assert/strict'; import assert from 'node:assert/strict';
import { parseTarget } from '../src/pure.js'; import { parseTarget, computeEstimate } from '../src/pure.js';
test('parseTarget: integer numbers', () => { test('parseTarget: integer numbers', () => {
assert.equal(parseTarget(25), 25); assert.equal(parseTarget(25), 25);
@@ -32,3 +32,38 @@ test('parseTarget: rejects invalid input', () => {
assert.equal(parseTarget('1.5.5M'), null); assert.equal(parseTarget('1.5.5M'), null);
assert.equal(parseTarget('0'), null); assert.equal(parseTarget('0'), null);
}); });
test('computeEstimate: typical case', () => {
const r = computeEstimate(14_328_501, 25_000_000, 247, 4520);
assert.equal(r.remaining, 10_671_499);
assert.equal(r.trainsToGo, 43_205);
assert.equal(r.days, 2_361);
assert.ok(r.eta instanceof Date);
});
test('computeEstimate: target reached', () => {
const r = computeEstimate(25_000_000, 25_000_000, 247, 4520);
assert.equal(r.remaining, 0);
assert.equal(r.trainsToGo, 0);
assert.equal(r.days, 0);
assert.equal(r.eta, null);
});
test('computeEstimate: target below current', () => {
const r = computeEstimate(30_000_000, 25_000_000, 247, 4520);
assert.equal(r.remaining, 0);
assert.equal(r.trainsToGo, 0);
assert.equal(r.days, 0);
assert.equal(r.eta, null);
});
test('computeEstimate: zero perTrain or perDay does not crash', () => {
const a = computeEstimate(100, 200, 0, 50);
assert.equal(a.trainsToGo, 0);
assert.equal(a.days, 2);
const b = computeEstimate(100, 200, 50, 0);
assert.equal(b.trainsToGo, 2);
assert.equal(b.days, 0);
assert.equal(b.eta, null);
});