Files
calculation/test/test.both.ts

246 lines
8.4 KiB
TypeScript
Raw Normal View History

import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as calculation from '../ts/index.js';
tap.test('Calculator class should perform basic arithmetic with decimal precision', async () => {
const calc = new calculation.Calculator({ precision: 10 });
// Test addition
const sum = calc.add(0.1, 0.2);
expect(calc.toString(sum)).toEqual('0.3');
// Test subtraction
const diff = calc.subtract(1, 0.9);
expect(calc.toString(diff)).toEqual('0.1');
// Test multiplication
const product = calc.multiply(0.1, 0.2);
expect(calc.toString(product)).toEqual('0.02');
// Test division
const quotient = calc.divide(1, 3);
expect(calc.toString(quotient)).toEqual('0.3333333333');
// Test power
const power = calc.power(2, 3);
expect(calc.toNumber(power)).toEqual(8);
// Test square root
const sqrt = calc.sqrt(9);
expect(calc.toNumber(sqrt)).toEqual(3);
// Test rounding
const rounded = calc.round(3.14159, 2);
expect(calc.toString(rounded)).toEqual('3.14');
});
tap.test('Financial class should calculate time value of money correctly', async () => {
const financial = new calculation.Financial();
// Test Present Value
const pv = financial.presentValue(1000, 0.05, 5);
expect(financial.round(pv, 2).toString()).toEqual('783.53');
// Test Future Value
const fv = financial.futureValue(1000, 0.05, 5);
expect(financial.round(fv, 2).toString()).toEqual('1276.28');
// Test Payment
const pmt = financial.payment(10000, 0.05/12, 60);
expect(financial.round(pmt, 2).toString()).toEqual('188.71');
// Test NPV
const cashFlows = [-1000, 300, 300, 300, 300, 300];
const npv = financial.npv(0.1, cashFlows);
expect(financial.round(npv, 2).toString()).toEqual('137.24');
// Test periods calculation
const periods = financial.periods(1000, 2000, 0.08);
expect(financial.round(periods, 2).toString()).toEqual('9.01');
// Test rate calculation
const rate = financial.rate(1000, 2000, 10);
expect(financial.round(rate, 4).toString()).toEqual('0.0718');
});
tap.test('Financial class should calculate IRR correctly', async () => {
const financial = new calculation.Financial();
// Test basic IRR
const cashFlows = [-1000, 200, 300, 400, 500];
const irr = financial.irr(cashFlows);
expect(financial.round(irr, 4).toString()).toEqual('0.1283');
// Test MIRR
const mirr = financial.mirr(cashFlows, 0.1, 0.12);
expect(financial.round(mirr, 4).toString()).toEqual('0.1256');
});
tap.test('Interest class should calculate different types of interest', async () => {
const interest = new calculation.Interest();
// Test simple interest
const simple = interest.simple(1000, 0.05, 2);
expect(interest.toString(simple)).toEqual('100');
// Test simple interest amount
const simpleAmount = interest.simpleAmount(1000, 0.05, 2);
expect(interest.toString(simpleAmount)).toEqual('1100');
// Test compound interest annually
const compound = interest.compound(1000, 0.05, 2, 'annually');
expect(interest.round(compound, 2).toString()).toEqual('102.5');
// Test compound interest monthly
const compoundMonthly = interest.compound(1000, 0.12, 1, 'monthly');
expect(interest.round(compoundMonthly, 2).toString()).toEqual('126.83');
// Test continuous compound interest
const continuous = interest.compound(1000, 0.05, 2, 'continuous');
expect(interest.round(continuous, 2).toString()).toEqual('105.17');
// Test effective annual rate
const ear = interest.effectiveAnnualRate(0.12, 'monthly');
expect(interest.round(ear, 4).toString()).toEqual('0.1268');
// Test real rate
const realRate = interest.realRate(0.08, 0.03);
expect(interest.round(realRate, 4).toString()).toEqual('0.0485');
// Test Rule of 72
const rule72 = interest.ruleOf72(8);
expect(interest.toString(rule72)).toEqual('9');
});
tap.test('Amortization class should generate correct loan schedules', async () => {
const amortization = new calculation.Amortization();
// Test basic loan schedule
const schedule = amortization.schedule({
principal: 10000,
annualRate: 0.05,
termYears: 2
});
expect(schedule.payments).toHaveLength(24);
expect(amortization.round(schedule.monthlyPayment, 2).toString()).toEqual('438.71');
expect(amortization.round(schedule.totalInterest, 2).toString()).toEqual('529.13');
// Test remaining balance
const balance = amortization.remainingBalance(10000, 0.05, 24, 12);
expect(amortization.round(balance, 2).toString()).toEqual('5124.71');
// Test max loan amount
const maxLoan = amortization.maxLoanAmount(1000, 0.05, 360);
expect(amortization.round(maxLoan, 2).toString()).toEqual('186281.62');
// Test LTV ratio
const ltv = amortization.ltv(80000, 100000);
expect(amortization.toString(ltv)).toEqual('0.8');
// Test DTI ratio
const dti = amortization.dti(1500, 5000);
expect(amortization.toString(dti)).toEqual('0.3');
});
tap.test('Currency class should handle currency operations correctly', async () => {
const currency = new calculation.Currency();
// Set up exchange rates
currency.setExchangeRate('USD', 'EUR', 0.85);
currency.setExchangeRate('USD', 'GBP', 0.73);
// Test currency conversion
const converted = currency.convert(100, 'USD', 'EUR');
expect(currency.toString(converted)).toEqual('85');
// Test inverse conversion
const inverse = currency.convert(85, 'EUR', 'USD');
expect(currency.round(inverse, 2).toString()).toEqual('100');
// Test currency formatting
const formatted = currency.format(1234.56, 'USD');
expect(formatted).toEqual('$1,234.56');
const formattedEur = currency.format(1234.56, 'EUR');
expect(formattedEur).toEqual('€1.234,56');
const formattedJpy = currency.format(1234.56, 'JPY');
expect(formattedJpy).toEqual('¥1,235');
// Test parsing
const parsed = currency.parse('$1,234.56', 'USD');
expect(currency.toString(parsed)).toEqual('1234.56');
// Test money operations
const money1 = currency.money(100, 'USD');
const money2 = currency.money(50, 'USD');
const sum = currency.addMoney(money1, money2);
expect(currency.toString(sum.amount)).toEqual('150');
// Test percentage calculations
const percentage = currency.percentage(100, 15);
expect(currency.toString(percentage)).toEqual('15');
// Test tax calculations
const withTax = currency.withTax(100, 0.08);
expect(currency.toString(withTax)).toEqual('108');
const extracted = currency.extractTax(108, 0.08);
expect(currency.round(extracted.baseAmount, 2).toString()).toEqual('100');
expect(currency.round(extracted.taxAmount, 2).toString()).toEqual('8');
});
tap.test('Edge cases and error handling', async () => {
const calc = new calculation.Calculator();
const financial = new calculation.Financial();
const interest = new calculation.Interest();
const amortization = new calculation.Amortization();
const currency = new calculation.Currency();
// Test division by zero
expect(() => calc.divide(10, 0)).toThrow();
// Test zero interest rate in periods calculation
expect(() => financial.periods(1000, 2000, 0)).toThrow();
// Test zero periods in rate calculation
expect(() => financial.rate(1000, 2000, 0)).toThrow();
// Test doubling time with zero rate
expect(() => interest.doubleTime(0)).toThrow();
// Test Rule of 72 with zero rate
expect(() => interest.ruleOf72(0)).toThrow();
// Test LTV with zero property value
expect(() => amortization.ltv(100000, 0)).toThrow();
// Test DTI with zero income
expect(() => amortization.dti(1000, 0)).toThrow();
// Test currency conversion without exchange rate
expect(() => currency.convert(100, 'USD', 'JPY')).toThrow();
// Test adding money with different currencies
const usdMoney = currency.money(100, 'USD');
const eurMoney = currency.money(100, 'EUR');
expect(() => currency.addMoney(usdMoney, eurMoney)).toThrow();
});
tap.test('Precision and accuracy tests', async () => {
const calc = new calculation.Calculator({ precision: 20 });
// Test high precision calculation
const result = calc.divide(1, 3);
expect(calc.toString(result)).toEqual('0.33333333333333333333');
// Test very small numbers
const small = calc.multiply('0.000000001', '0.000000001');
expect(calc.toString(small)).toEqual('1e-18');
// Test very large numbers
const large = calc.multiply('1000000000000', '1000000000000');
expect(calc.toString(large)).toEqual('1e+24');
});
tap.start();