Move tests to __tests__ folders, add unit tests for shared utils and services

Restructure tests into __tests__/ directories at package root so they can
be excluded from production builds. Add unit tests for dates, currency,
lookup service, payment method default logic, and tax exemption state
transitions.
This commit is contained in:
Ryan Moon
2026-03-27 21:14:42 -05:00
parent 0a2d6e23af
commit 01d6ff3fa3
10 changed files with 545 additions and 6 deletions

View File

@@ -0,0 +1,68 @@
import { describe, it, expect } from 'bun:test'
import { formatCurrency, roundCurrency, toCents, toDollars } from '../../src/utils/currency.js'
describe('formatCurrency', () => {
it('formats whole dollars', () => {
expect(formatCurrency(100)).toBe('$100.00')
})
it('formats cents', () => {
expect(formatCurrency(49.99)).toBe('$49.99')
})
it('formats zero', () => {
expect(formatCurrency(0)).toBe('$0.00')
})
it('formats thousands with comma', () => {
expect(formatCurrency(1234.56)).toBe('$1,234.56')
})
it('formats negative amounts', () => {
expect(formatCurrency(-25.50)).toBe('-$25.50')
})
})
describe('roundCurrency', () => {
it('rounds to 2 decimal places', () => {
expect(roundCurrency(1.005)).toBe(1.01)
expect(roundCurrency(1.004)).toBe(1)
})
it('handles already-rounded values', () => {
expect(roundCurrency(10.50)).toBe(10.50)
})
it('handles zero', () => {
expect(roundCurrency(0)).toBe(0)
})
})
describe('toCents', () => {
it('converts dollars to cents', () => {
expect(toCents(1.00)).toBe(100)
expect(toCents(49.99)).toBe(4999)
})
it('rounds fractional cents', () => {
// 1.005 * 100 = 100.49999... in IEEE 754, so Math.round gives 100
// This is a known floating point limitation — use toCents(roundCurrency(x)) for precision
expect(toCents(1.005)).toBe(100)
expect(toCents(1.006)).toBe(101)
})
it('handles zero', () => {
expect(toCents(0)).toBe(0)
})
})
describe('toDollars', () => {
it('converts cents to dollars', () => {
expect(toDollars(100)).toBe(1.00)
expect(toDollars(4999)).toBe(49.99)
})
it('handles zero', () => {
expect(toDollars(0)).toBe(0)
})
})

View File

@@ -0,0 +1,79 @@
import { describe, it, expect } from 'bun:test'
import { isMinor, capBillingDay, todayISO } from '../../src/utils/dates.js'
describe('isMinor', () => {
it('returns true for a child born 10 years ago', () => {
const dob = new Date()
dob.setFullYear(dob.getFullYear() - 10)
expect(isMinor(dob)).toBe(true)
})
it('returns false for an adult born 25 years ago', () => {
const dob = new Date()
dob.setFullYear(dob.getFullYear() - 25)
expect(isMinor(dob)).toBe(false)
})
it('returns true for someone turning 18 later this year', () => {
const dob = new Date()
dob.setFullYear(dob.getFullYear() - 18)
dob.setMonth(dob.getMonth() + 1) // birthday is next month
expect(isMinor(dob)).toBe(true)
})
it('returns false for someone who turned 18 earlier this year', () => {
const dob = new Date()
dob.setFullYear(dob.getFullYear() - 18)
dob.setMonth(dob.getMonth() - 1) // birthday was last month
expect(isMinor(dob)).toBe(false)
})
it('returns false on their 18th birthday', () => {
const dob = new Date()
dob.setFullYear(dob.getFullYear() - 18)
expect(isMinor(dob)).toBe(false)
})
it('accepts a string date', () => {
expect(isMinor('2000-01-01')).toBe(false)
expect(isMinor('2020-01-01')).toBe(true)
})
it('returns true for a newborn', () => {
expect(isMinor(new Date())).toBe(true)
})
})
describe('capBillingDay', () => {
it('caps at 28', () => {
expect(capBillingDay(31)).toBe(28)
expect(capBillingDay(29)).toBe(28)
})
it('floors at 1', () => {
expect(capBillingDay(0)).toBe(1)
expect(capBillingDay(-5)).toBe(1)
})
it('passes through valid days', () => {
expect(capBillingDay(15)).toBe(15)
expect(capBillingDay(1)).toBe(1)
expect(capBillingDay(28)).toBe(28)
})
it('floors fractional days', () => {
expect(capBillingDay(15.7)).toBe(15)
})
})
describe('todayISO', () => {
it('returns YYYY-MM-DD format', () => {
const result = todayISO()
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/)
})
it('matches today', () => {
const expected = new Date().toISOString().split('T')[0]
expect(todayISO()).toBe(expected)
})
})