Skip to content

Commit 3555217

Browse files
fix(auth): guard blank rawCap + short-circuit cap≤0 in computeSignupCapacity
- Blank/whitespace rawCap (e.g. SIGN_CAP="") now treated as uncapped (null) instead of coercing to 0 via Number('') - cap ≤ 0 short-circuits countFn (remaining is deterministically 0, no DB hit) - Add unit tests: empty string cap, whitespace cap, cap=0 no-count assertion
1 parent 7302eef commit 3555217

2 files changed

Lines changed: 19 additions & 6 deletions

File tree

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
/**
22
* @desc Compute the public beta-capacity view for the auth config endpoint:
33
* the configured cap and how many seats remain. Skips the (collection-wide)
4-
* count entirely when uncapped, so the common case stays a no-op.
5-
* @param {number|string|null|undefined} rawCap - config.sign.cap (null/undefined/non-numeric = uncapped; 0 = fully closed, 0 seats)
4+
* count entirely when uncapped or when cap ≤ 0 (remaining is deterministically 0).
5+
* @param {number|string|null|undefined} rawCap - config.sign.cap (null/undefined/non-numeric/blank = uncapped; 0 = fully closed, 0 seats)
66
* @param {() => Promise<number>} countFn - async total-account counter (UserService.count)
77
* @returns {Promise<{cap: number|null, remaining: number|null}>}
88
*/
99
export const computeSignupCapacity = async (rawCap, countFn) => {
10-
const cap = rawCap != null ? Number(rawCap) : null;
10+
const cap = rawCap != null && String(rawCap).trim() !== '' ? Number(rawCap) : null;
1111
if (cap == null || !Number.isFinite(cap)) return { cap: null, remaining: null };
12+
if (cap <= 0) return { cap, remaining: 0 };
1213
const remaining = Math.max(0, cap - (await countFn()));
1314
return { cap, remaining };
1415
};

modules/auth/tests/auth.signupCapacity.unit.tests.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,22 @@ describe('computeSignupCapacity', () => {
3030
expect(countFn).not.toHaveBeenCalled();
3131
});
3232

33-
test('cap = 0 → fully closed: {cap:0, remaining:0} and count IS called', async () => {
34-
const countFn = jest.fn().mockResolvedValue(0);
33+
test('empty string cap → treated as uncapped (not coerced to 0)', async () => {
34+
const countFn = jest.fn();
35+
await expect(computeSignupCapacity('', countFn)).resolves.toEqual({ cap: null, remaining: null });
36+
expect(countFn).not.toHaveBeenCalled();
37+
});
38+
39+
test('whitespace-only cap → treated as uncapped (not coerced to 0)', async () => {
40+
const countFn = jest.fn();
41+
await expect(computeSignupCapacity(' ', countFn)).resolves.toEqual({ cap: null, remaining: null });
42+
expect(countFn).not.toHaveBeenCalled();
43+
});
44+
45+
test('cap = 0 → fully closed: {cap:0, remaining:0} and count is NOT called (short-circuit)', async () => {
46+
const countFn = jest.fn();
3547
await expect(computeSignupCapacity(0, countFn)).resolves.toEqual({ cap: 0, remaining: 0 });
36-
expect(countFn).toHaveBeenCalled();
48+
expect(countFn).not.toHaveBeenCalled();
3749
});
3850

3951
test('numeric string cap ("42") → coerced: {cap:42, remaining:40}', async () => {

0 commit comments

Comments
 (0)