1
0
mirror of https://gitlab.com/MisterBiggs/secure-act-2.0.git synced 2025-08-17 16:24:43 +00:00
Files
secure-act-2.0/calculations.test.js
2025-08-10 23:00:30 -06:00

201 lines
5.4 KiB
JavaScript

/**
* Tests for Student Loan Matching Calculator
* Run with: node calculations.test.js
*/
const {
calculatePayoffYears,
calculateRemainingBalance,
calculateRetirementValue,
calculateNetWorthComparison
} = require('./calculations.js');
// Test utilities
let testsPassed = 0;
let testsFailed = 0;
function assert(condition, message) {
if (condition) {
console.log(`${message}`);
testsPassed++;
} else {
console.error(`${message}`);
testsFailed++;
}
}
function assertClose(actual, expected, tolerance = 0.01, message = '') {
const diff = Math.abs(actual - expected);
const pass = diff <= tolerance;
if (pass) {
console.log(`${message} (${actual}${expected})`);
testsPassed++;
} else {
console.error(`${message} (${actual} != ${expected}, diff: ${diff})`);
testsFailed++;
}
}
// Test calculatePayoffYears
console.log('\n=== Testing calculatePayoffYears ===');
assert(
calculatePayoffYears(30000, 500) === 6,
'Should calculate ~6 years for $30k debt at $500/month (5% interest)'
);
assert(
calculatePayoffYears(30000, 0) === 0,
'Should return 0 for zero payment'
);
assert(
calculatePayoffYears(30000, 100) === 50,
'Should return 50 (max) for payment below interest'
);
// Test calculateRemainingBalance
console.log('\n=== Testing calculateRemainingBalance ===');
const balance5Years = calculateRemainingBalance(30000, 500, 60, 84);
assertClose(
calculateRemainingBalance(30000, 500, 60, 6 * 12),
4498,
100,
'After 5 years of $500 payments on $30k, should have ~$4.5k remaining (5% interest)'
);
assert(
calculateRemainingBalance(30000, 500, 0, 84) === 30000,
'At month 0, balance should equal original'
);
assert(
calculateRemainingBalance(30000, 500, 84, 84) === 0,
'At final month, balance should be 0'
);
assert(
calculateRemainingBalance(30000, 500, 100, 84) === 0,
'After loan term, balance should be 0'
);
// Test calculateRetirementValue
console.log('\n=== Testing calculateRetirementValue ===');
const value10Years = calculateRetirementValue(5000, 10, 10);
assertClose(
value10Years,
73918,
100,
'$5k/year for 10 years at 7% should grow to ~$74k'
);
const value30Years = calculateRetirementValue(5000, 10, 30);
assertClose(
value30Years,
286039,
100,
'$5k/year for 10 years, growing for 30 total years should be ~$286k'
);
// Test full calculation scenario
console.log('\n=== Testing Full Scenario ===');
const scenario1 = calculateNetWorthComparison({
salary: 60000,
totalDebt: 30000,
monthlyPayment: 500,
matchRate: 5,
currentAge: 25
});
console.log('\nScenario 1 Results:');
console.log(` Payoff years: ${scenario1.payoffYears}`);
console.log(` Annual match: $${scenario1.annualMatch}`);
console.log(` Retirement with program: $${scenario1.totalRetirementWith.toLocaleString()}`);
console.log(` Retirement without program: $${scenario1.totalRetirementWithout.toLocaleString()}`);
console.log(` Difference: $${scenario1.retirementDifference.toLocaleString()}`);
assert(
scenario1.payoffYears === 6,
'Should take 6 years to pay off $30k at $500/month (5% interest)'
);
assert(
scenario1.annualMatch === 3000,
'Annual match should be $3000 (5% of $60k salary)'
);
assert(
scenario1.retirementDifference > 0,
'With program should result in more retirement savings'
);
// Test edge case: very high payment
const scenario2 = calculateNetWorthComparison({
salary: 100000,
totalDebt: 20000,
monthlyPayment: 2000,
matchRate: 4,
currentAge: 30
});
console.log('\nScenario 2 Results (High Payment):');
console.log(` Payoff years: ${scenario2.payoffYears}`);
console.log(` Annual match: $${scenario2.annualMatch}`);
assert(
scenario2.payoffYears <= 2,
'High payment should result in quick payoff'
);
assert(
scenario2.annualMatch === 4000,
'Annual match should be capped at 4% of salary'
);
// Test the growth rates after payoff
console.log('\n=== Testing Growth Rates After Payoff ===');
const scenario3 = calculateNetWorthComparison({
salary: 70000,
totalDebt: 40000,
monthlyPayment: 600,
matchRate: 5,
currentAge: 28
});
// Find the index right after payoff
const payoffIndex = Math.ceil((scenario3.payoffYears / 2));
if (payoffIndex < scenario3.withProgram.length - 1) {
const withGrowth = scenario3.withProgram[payoffIndex + 1] - scenario3.withProgram[payoffIndex];
const withoutGrowth = scenario3.withoutProgram[payoffIndex + 1] - scenario3.withoutProgram[payoffIndex];
console.log(`\nGrowth rate comparison after payoff:`);
console.log(` With program growth: $${withGrowth}`);
console.log(` Without program growth: $${withoutGrowth}`);
// After payoff, both should be contributing the same amount, so growth should be similar
// The "with" program will have a larger base, but the rate of change should be close
const growthRatio = withoutGrowth > 0 ? withGrowth / withoutGrowth : 0;
assertClose(
growthRatio,
1.5,
1.0,
'Growth rates after payoff should be relatively similar'
);
}
// Summary
console.log('\n=== Test Summary ===');
console.log(`Tests passed: ${testsPassed}`);
console.log(`Tests failed: ${testsFailed}`);
if (testsFailed === 0) {
console.log('\n✅ All tests passed!');
process.exit(0);
} else {
console.log('\n❌ Some tests failed');
process.exit(1);
}