← back to Boomer Calculator

tests/calculations.cjs

38 lines

const { test } = require('node:test')
const assert = require('node:assert/strict')
const { evaluateExpression: calc, monthlyPayment, bodyMassIndex, tipAmounts, readNumber } = require('../tmp/math-tests/calculations.js')
const close = (actual, expected, epsilon = 1e-8) => assert.ok(Math.abs(actual - expected) < epsilon, `${actual} != ${expected}`)
test('arithmetic precedence, parentheses, unary signs, decimals and continued scientific notation', () => {
  for (const [input, expected] of [['2+3*4',14],['(2+3)*4',20],['-2*-3',6],['.5+.25',.75],['1e+12+1',1000000000001],['8/4/2',1]]) assert.equal(calc(input), expected)
})
test('sine uses degrees including nested expressions and negative angles', () => {
  close(calc('sin(30)'), .5); close(calc('sin(90)'),1); close(calc('sin(-90)'),-1)
  assert.equal(calc('sin(180)'),0); close(calc('2*sin(30+60)'),2)
})
test('invalid and unsafe expressions produce errors', () => {
  for (const input of ['1/0','0/0','(2+3','2+','2(3)','sin30','process.exit()','1e999','1'.repeat(501)]) assert.throws(() => calc(input), Error)
})
test('mortgage known payment, zero rate, full down payment and tiny rate', () => {
  close(monthlyPayment(500000,100000,30,6.5),2528.2720939718)
  close(monthlyPayment(120000,0,20,0),500)
  assert.equal(monthlyPayment(500000,500000,30,6.5),0)
  close(monthlyPayment(120000,0,20,1e-12),500,1e-7)
  for (const args of [[0,0,30,5],[100,-1,30,5],[100,101,30,5],[100,0,0,5],[100,0,30,-1],[Infinity,0,30,5]]) assert.throws(() => monthlyPayment(...args))
})
test('BMI metric and imperial conversions describe same measurements', () => {
  close(bodyMassIndex(70,175,'metric'),22.857142857142858)
  close(bodyMassIndex(150,67,'imperial'),23.5,.05)
  close(bodyMassIndex(150,67,'imperial'),bodyMassIndex(150*.45359237,67*2.54,'metric'))
  assert.throws(() => bodyMassIndex(70,0,'metric')); assert.throws(() => bodyMassIndex(-70,175,'metric'))
})
test('tip and splits handle zero and fractional cents consistently', () => {
  assert.deepEqual(tipAmounts(100,20,4),{ tip:20,total:120,perPerson:30 })
  assert.deepEqual(tipAmounts(0,0,1),{ tip:0,total:0,perPerson:0 })
  assert.deepEqual(tipAmounts(10.01,15,3),{tip:1.5,total:11.51,perPerson:3.84})
  for (const args of [[10,15,0],[10,15,1.5],[-1,15,1],[10,-1,1],[1e20,15,1]]) assert.throws(() => tipAmounts(...args))
})
test('numeric fields reject empty or nonfinite values', () => {
  assert.equal(readNumber('0','rate'),0)
  for(const input of ['',' ','abc','Infinity']) assert.throws(() => readNumber(input,'rate'))
})