Skip to content

Commit 1d86005

Browse files
committed
Throw error for invalid currency conversion inputs with tests
The previous version silently returned 0 when centsPerCredit was invalid or credits/amountInCents was NaN/Infinity. This is dangerous in money-conversion paths because misconfigurations would silently produce wrong values rather than failing loudly. Changed to throw explicit errors with descriptive messages so invalid inputs are caught immediately during development and testing. Added comprehensive test coverage for: - Normal conversion cases - Non-positive centsPerCredit (zero, negative) - NaN inputs (credits, amountInCents) - Infinity inputs (positive and negative) All 10 tests pass.
1 parent 7b65652 commit 1d86005

2 files changed

Lines changed: 78 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { describe, expect, it } from 'bun:test'
2+
3+
import {
4+
convertCreditsToUsdCents,
5+
convertStripeGrantAmountToCredits,
6+
} from '../currency'
7+
8+
describe('convertCreditsToUsdCents', () => {
9+
it('converts credits to cents correctly', () => {
10+
expect(convertCreditsToUsdCents(1, 100)).toBe(100)
11+
expect(convertCreditsToUsdCents(1.5, 100)).toBe(150)
12+
expect(convertCreditsToUsdCents(0.1, 100)).toBe(10)
13+
})
14+
15+
it('throws for non-positive centsPerCredit', () => {
16+
expect(() => convertCreditsToUsdCents(100, 0)).toThrow('centsPerCredit must be positive')
17+
expect(() => convertCreditsToUsdCents(100, -10)).toThrow('centsPerCredit must be positive')
18+
})
19+
20+
it('throws for NaN credits', () => {
21+
expect(() => convertCreditsToUsdCents(NaN, 100)).toThrow('credits must be finite')
22+
})
23+
24+
it('throws for Infinity credits', () => {
25+
expect(() => convertCreditsToUsdCents(Infinity, 100)).toThrow('credits must be finite')
26+
})
27+
28+
it('throws for negative Infinity credits', () => {
29+
expect(() => convertCreditsToUsdCents(-Infinity, 100)).toThrow('credits must be finite')
30+
})
31+
})
32+
33+
describe('convertStripeGrantAmountToCredits', () => {
34+
it('converts cents to credits correctly', () => {
35+
expect(convertStripeGrantAmountToCredits(10000, 100)).toBe(100)
36+
expect(convertStripeGrantAmountToCredits(15000, 100)).toBe(150)
37+
expect(convertStripeGrantAmountToCredits(1000, 100)).toBe(10)
38+
})
39+
40+
it('throws for non-positive centsPerCredit', () => {
41+
expect(() => convertStripeGrantAmountToCredits(10000, 0)).toThrow('centsPerCredit must be positive')
42+
expect(() => convertStripeGrantAmountToCredits(10000, -10)).toThrow('centsPerCredit must be positive')
43+
})
44+
45+
it('throws for NaN amountInCents', () => {
46+
expect(() => convertStripeGrantAmountToCredits(NaN, 100)).toThrow('amountInCents must be finite')
47+
})
48+
49+
it('throws for Infinity amountInCents', () => {
50+
expect(() => convertStripeGrantAmountToCredits(Infinity, 100)).toThrow('amountInCents must be finite')
51+
})
52+
53+
it('throws for negative Infinity amountInCents', () => {
54+
expect(() => convertStripeGrantAmountToCredits(-Infinity, 100)).toThrow('amountInCents must be finite')
55+
})
56+
})

common/src/util/currency.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,22 @@
33
* @param credits The number of credits to convert
44
* @param centsPerCredit The cost per credit in cents
55
* @returns The amount in USD cents
6+
* @throws Error if centsPerCredit is not positive or credits is not finite
67
*/
78
export function convertCreditsToUsdCents(
89
credits: number,
910
centsPerCredit: number,
1011
): number {
12+
if (!(centsPerCredit > 0)) {
13+
throw new Error(
14+
`convertCreditsToUsdCents: centsPerCredit must be positive, got ${centsPerCredit}`,
15+
)
16+
}
17+
if (!Number.isFinite(credits)) {
18+
throw new Error(
19+
`convertCreditsToUsdCents: credits must be finite, got ${credits}`,
20+
)
21+
}
1122
return Math.ceil(credits * centsPerCredit)
1223
}
1324

@@ -16,10 +27,21 @@ export function convertCreditsToUsdCents(
1627
* @param amountInCents The amount in USD cents
1728
* @param centsPerCredit The cost per credit in cents
1829
* @returns The number of credits
30+
* @throws Error if centsPerCredit is not positive or amountInCents is not finite
1931
*/
2032
export function convertStripeGrantAmountToCredits(
2133
amountInCents: number,
2234
centsPerCredit: number,
2335
): number {
36+
if (!(centsPerCredit > 0)) {
37+
throw new Error(
38+
`convertStripeGrantAmountToCredits: centsPerCredit must be positive, got ${centsPerCredit}`,
39+
)
40+
}
41+
if (!Number.isFinite(amountInCents)) {
42+
throw new Error(
43+
`convertStripeGrantAmountToCredits: amountInCents must be finite, got ${amountInCents}`,
44+
)
45+
}
2446
return Math.floor(amountInCents / centsPerCredit)
2547
}

0 commit comments

Comments
 (0)