Skip to content

Commit b931246

Browse files
test(api): verify refresh-policy empty inputs and boundaries
1 parent dcaafea commit b931246

1 file changed

Lines changed: 87 additions & 0 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { describe, it, expect, beforeEach, vi } from 'vitest';
2+
import { RefreshPolicy } from './refresh-policy';
3+
4+
// Mock the quota monitor to avoid interference
5+
vi.mock('./quota-monitor', () => ({
6+
quotaMonitor: {
7+
isQuotaLow: vi.fn(() => false),
8+
incrementRefreshCount: vi.fn(),
9+
},
10+
}));
11+
12+
describe('RefreshPolicy - Edge Cases & Empty/Missing Inputs', () => {
13+
let policy: RefreshPolicy;
14+
15+
beforeEach(() => {
16+
policy = RefreshPolicy.getInstance();
17+
policy.reset();
18+
vi.clearAllMocks();
19+
});
20+
21+
it('Empty/Whitespace Usernames: handles empty and whitespace strings gracefully', () => {
22+
const emptyUser = '';
23+
const spaceUser = ' ';
24+
25+
// Should be allowed initially
26+
expect(policy.isRefreshAllowed(emptyUser)).toBe(true);
27+
expect(policy.isRefreshAllowed(spaceUser)).toBe(true);
28+
29+
// Record refresh
30+
policy.recordRefresh(emptyUser);
31+
32+
// Both should now be restricted because ' '.trim() === ''.trim()
33+
expect(policy.isRefreshAllowed(emptyUser)).toBe(false);
34+
expect(policy.isRefreshAllowed(spaceUser)).toBe(false);
35+
});
36+
37+
it('Negative Cooldown Boundaries: clamps negative cooldown durations to 0', () => {
38+
policy.setCooldown(-5000); // Should be clamped to 0
39+
policy.recordRefresh('testuser');
40+
41+
// Since cooldown is clamped to 0, refresh is immediately allowed again
42+
expect(policy.isRefreshAllowed('testuser')).toBe(true);
43+
expect(policy.getRemainingCooldown('testuser')).toBe(0);
44+
});
45+
46+
it('Unrecorded Users Fallback: returns safe fallback value (0) for unrecorded users', () => {
47+
const remaining = policy.getRemainingCooldown('never-seen-before');
48+
expect(remaining).toBe(0); // Should be exactly 0, not undefined/NaN
49+
50+
// Ensure we aren't getting false negatives
51+
expect(policy.isRefreshAllowed('never-seen-before')).toBe(true);
52+
});
53+
54+
it('Missing/Undefined Parameters: throws TypeError predictably when passed undefined/null', () => {
55+
// Bypassing TS to simulate runtime missing parameters in JS environments
56+
const undefinedUser = undefined as unknown as string;
57+
const nullUser = null as unknown as string;
58+
59+
// We expect it to throw because .trim() is called on undefined/null
60+
expect(() => policy.isRefreshAllowed(undefinedUser)).toThrowError(TypeError);
61+
expect(() => policy.isRefreshAllowed(nullUser)).toThrowError(TypeError);
62+
63+
expect(() => policy.recordRefresh(undefinedUser)).toThrowError(TypeError);
64+
expect(() => policy.getRemainingCooldown(nullUser)).toThrowError(TypeError);
65+
});
66+
67+
it('Singleton Reset Stability: clears all state and restores defaults on reset', () => {
68+
// Change state
69+
policy.setCooldown(10000);
70+
policy.recordRefresh('user-a');
71+
72+
// Verify state was changed
73+
expect(policy.isRefreshAllowed('user-a')).toBe(false);
74+
75+
// Reset
76+
policy.reset();
77+
78+
// Verify map is completely cleared
79+
expect(policy.isRefreshAllowed('user-a')).toBe(true);
80+
expect(policy.getRemainingCooldown('user-a')).toBe(0);
81+
82+
// Verify cooldown is restored to default (5 * 60 * 1000 = 300000ms)
83+
policy.recordRefresh('user-a');
84+
// It should be 300000 right after recording (elapsed ~ 0)
85+
expect(policy.getRemainingCooldown('user-a')).toBe(300000);
86+
});
87+
});

0 commit comments

Comments
 (0)