Skip to content

Commit

Permalink
Use CSV for tax test
Browse files Browse the repository at this point in the history
  • Loading branch information
ssigwart committed Mar 6, 2022
1 parent 89778cc commit 7bd83dd
Show file tree
Hide file tree
Showing 3 changed files with 2,182 additions and 228 deletions.
15 changes: 12 additions & 3 deletions src/forms/Y2021/irsForms/TaxTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,24 @@ const computeTax =
// Otherwise max income is inside this bracket,
// add the tax on the amount falling in this bracket

if (income < 5) {
return 0
}
// If income is between $5 and $25, tax table computes rate at midpoint of $5 ranges
if (income >= 5 && income < 25) {
income = Math.floor(income)
const over5 = income % 5
income += 2.5 - over5
}
// If income is between $25 and $3,000, tax table computes rate at midpoint of $25 ranges
if (income >= 25 && income < 3000) {
income = Math.round(income)
else if (income >= 25 && income < 3000) {
income = Math.floor(income)
const over25 = income % 25
income += 12.5 - over25
}
// If income is between $3,000 and $100,000, tax table computes rate at midpoint of $50 ranges
else if (income >= 3000 && income < 100000) {
income = Math.round(income)
income = Math.floor(income)
const over50 = income % 50
income += 25 - over50
}
Expand Down
332 changes: 107 additions & 225 deletions src/forms/Y2021/tests/taxRates.test.ts
Original file line number Diff line number Diff line change
@@ -1,253 +1,135 @@
import { FilingStatus } from 'ustaxes/core/data'
import { CURRENT_YEAR } from '../data/federal'
import { computeOrdinaryTax } from '../irsForms/TaxTable'
import fs from 'fs/promises'
import { parseCsv } from 'ustaxes/data/csvImport'
import { parseFormNumber } from 'ustaxes/core/util'

async function getTaxTable() {
const path = './src/forms/Y2021/tests/taxTable.csv'
const taxTableCsv = (await fs.readFile(path)).toString('utf-8')
const rows: Array<number[]> = []
await parseCsv(taxTableCsv, (r: string[]) => {
rows.push(r.map((s) => parseFormNumber(s) ?? 0))
return []
})

// Remove heading row
rows.shift()

return rows
}

function expectTax(status: FilingStatus, amount: number, tax: number) {
const computedTax = Math.round(computeOrdinaryTax(status, amount))
// Add a prefix so it's easy to see which one was wrong
const prefix = 'Tax on ' + amount + ' = '
expect(prefix + computedTax).toEqual(prefix + tax)
}

function expectTaxUnder100KRange(
status: FilingStatus,
min: number,
max: number,
tax: number
) {
const diff = max - min
const quarter = Math.round((diff / 4) * 100) / 100
expectTax(status, min, tax)
expectTax(status, min + quarter, tax)
expectTax(status, min + 2 * quarter, tax)
expectTax(status, min + 3 * quarter, tax)
expectTax(status, max, tax)
}

describe('Tax rates', () => {
it('test should be updated for new year', async () => {
// WARNING! Do not just chagne the year. Also update the expected tax amounts below!
// WARNING! Do not just change the year. Also update the CSV and expected tax amounts below!
expect(CURRENT_YEAR).toEqual(2021)
})
it('ordinary taxes for single status should be correct', async () => {
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 5))).toEqual(1)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 14))).toEqual(1)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 2075))).toEqual(209)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 2099))).toEqual(209)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 9949))).toEqual(993)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 9950))).toEqual(998)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 10000))).toEqual(1004)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 13700))).toEqual(1448)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 13749))).toEqual(1448)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 37000))).toEqual(4244)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 37049))).toEqual(4244)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 50000))).toEqual(6754)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 50049))).toEqual(6754)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 75000))).toEqual(12254)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 75049))).toEqual(12254)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 99950))).toEqual(18015)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 99999))).toEqual(18015)
const rows = await getTaxTable()
rows.forEach(([min, lessThan, tax]) => {
expectTaxUnder100KRange(FilingStatus.S, min, lessThan - 0.01, tax)
})

// Over $100,000
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 100000))).toEqual(
18021
)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 164925))).toEqual(
33603
)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 164926))).toEqual(
33603
)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 209425))).toEqual(
47843
)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 209426))).toEqual(
47843
)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 523600))).toEqual(
157804
)
expect(Math.round(computeOrdinaryTax(FilingStatus.S, 523601))).toEqual(
157805
)
const amounts = [
[100000, 18021],
[164925, 33603],
[164926, 33603],
[209425, 47843],
[209426, 47843],
[523600, 157804],
[523601, 157805]
]
amounts.forEach(([amount, tax]) => {
expectTax(FilingStatus.S, amount, tax)
})
})

it('ordinary taxes for married filing jointly status should be correct', async () => {
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 5))).toEqual(1)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 14))).toEqual(1)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 2075))).toEqual(209)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 2099))).toEqual(209)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 9949))).toEqual(993)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 9950))).toEqual(998)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 10000))).toEqual(
1003
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 13700))).toEqual(
1373
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 13749))).toEqual(
1373
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 37000))).toEqual(
4045
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 37049))).toEqual(
4045
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 50000))).toEqual(
5605
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 50049))).toEqual(
5605
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 75000))).toEqual(
8605
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 75049))).toEqual(
8605
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 99950))).toEqual(
13492
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 99999))).toEqual(
13492
)
const rows = await getTaxTable()
rows.forEach(([min, lessThan, , tax]) => {
expectTaxUnder100KRange(FilingStatus.MFJ, min, lessThan - 0.01, tax)
})

// Over $100,000
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 100000))).toEqual(
13497
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 172750))).toEqual(
29502
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 172751))).toEqual(
29502
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 329850))).toEqual(
67206
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 329851))).toEqual(
67206
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 418850))).toEqual(
95686
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 418851))).toEqual(
95686
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 628300))).toEqual(
168994
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFJ, 628301))).toEqual(
168994
)
const amounts = [
[100000, 13497],
[172750, 29502],
[172751, 29502],
[329850, 67206],
[329851, 67206],
[418850, 95686],
[418851, 95686],
[628300, 168994],
[628301, 168994]
]
amounts.forEach(([amount, tax]) => {
expectTax(FilingStatus.MFJ, amount, tax)
})
})

it('ordinary taxes for married filing separately status should be correct', async () => {
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 5))).toEqual(1)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 14))).toEqual(1)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 2075))).toEqual(209)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 2099))).toEqual(209)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 9949))).toEqual(993)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 9950))).toEqual(998)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 10000))).toEqual(
1004
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 13700))).toEqual(
1448
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 13749))).toEqual(
1448
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 37000))).toEqual(
4244
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 37049))).toEqual(
4244
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 50000))).toEqual(
6754
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 50049))).toEqual(
6754
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 75000))).toEqual(
12254
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 75049))).toEqual(
12254
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 99950))).toEqual(
18015
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 99999))).toEqual(
18015
)
const rows = await getTaxTable()
rows.forEach(([min, lessThan, , , tax]) => {
expectTaxUnder100KRange(FilingStatus.MFS, min, lessThan - 0.01, tax)
})

// Over $100,000
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 100000))).toEqual(
18021
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 164925))).toEqual(
33603
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 164926))).toEqual(
33603
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 209425))).toEqual(
47843
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 209426))).toEqual(
47843
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 314150))).toEqual(
84497
)
expect(Math.round(computeOrdinaryTax(FilingStatus.MFS, 314151))).toEqual(
84497
)
const amounts = [
[100000, 18021],
[164925, 33603],
[164926, 33603],
[209425, 47843],
[209426, 47843],
[314150, 84497],
[314151, 84497]
]
amounts.forEach(([amount, tax]) => {
expectTax(FilingStatus.MFS, amount, tax)
})
})

it('ordinary taxes for head of household status should be correct', async () => {
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 5))).toEqual(1)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 14))).toEqual(1)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 2075))).toEqual(209)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 2099))).toEqual(209)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 9949))).toEqual(993)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 9950))).toEqual(998)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 10000))).toEqual(
1003
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 13700))).toEqual(
1373
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 13749))).toEqual(
1373
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 37000))).toEqual(
4159
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 37049))).toEqual(
4159
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 50000))).toEqual(
5719
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 50049))).toEqual(
5719
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 75000))).toEqual(
10802
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 75049))).toEqual(
10802
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 99950))).toEqual(
16563
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 99999))).toEqual(
16563
)
const rows = await getTaxTable()
rows.forEach(([min, lessThan, , , , tax]) => {
expectTaxUnder100KRange(FilingStatus.HOH, min, lessThan - 0.01, tax)
})

// Over $100,000
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 100000))).toEqual(
16569
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 164900))).toEqual(
32145
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 164901))).toEqual(
32145
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 209400))).toEqual(
46385
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 209401))).toEqual(
46385
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 523600))).toEqual(
156355
)
expect(Math.round(computeOrdinaryTax(FilingStatus.HOH, 523601))).toEqual(
156355
)
const amounts = [
[100000, 16569],
[164900, 32145],
[164901, 32145],
[209400, 46385],
[209401, 46385],
[523600, 156355],
[523601, 156355]
]
amounts.forEach(([amount, tax]) => {
expectTax(FilingStatus.HOH, amount, tax)
})
})
})
Loading

0 comments on commit 7bd83dd

Please sign in to comment.