From faae7702aad0fa2f676859169939878505e7e3a6 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 1 Jun 2026 15:30:07 -0500 Subject: [PATCH] feat(pure): computeEstimate with safe division and target-reached handling --- src/pure.js | 13 +++++++++++++ tests/pure.test.js | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/pure.js b/src/pure.js index 3226484..7c960aa 100644 --- a/src/pure.js +++ b/src/pure.js @@ -23,3 +23,16 @@ export function parseTarget(input) { const multiplier = suffix ? SUFFIXES[suffix] : 1; 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 }; +} diff --git a/tests/pure.test.js b/tests/pure.test.js index 5683659..3b14f78 100644 --- a/tests/pure.test.js +++ b/tests/pure.test.js @@ -1,6 +1,6 @@ import { test } from 'node:test'; import assert from 'node:assert/strict'; -import { parseTarget } from '../src/pure.js'; +import { parseTarget, computeEstimate } from '../src/pure.js'; test('parseTarget: integer numbers', () => { assert.equal(parseTarget(25), 25); @@ -32,3 +32,38 @@ test('parseTarget: rejects invalid input', () => { assert.equal(parseTarget('1.5.5M'), 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); +});