Skip to content

Commit 33e9777

Browse files
test(utils): check boundary robustness of JSON response serializer (JhaSourav07#1646)
Fixes JhaSourav07#1556 ## Description Added a boundary test to verify that `trackUser` handles non-serializable JSON payloads gracefully without throwing. Also updated `tracking.ts` to wrap `JSON.stringify` in a try/catch to prevent uncaught exceptions. ## Changes Made - Added test for circular reference input in `utils/tracking.test.ts` - Wrapped `JSON.stringify` in try/catch in `utils/tracking.ts` ## Pillar - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Checklist - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally. - [x] `npm run format` and `npm run lint` pass locally ✅ - [x] `npm run test` passes locally ✅ - [x] My commits follow Conventional Commits format. - [x] I have starred the repo. - [x] I have only one commit in this PR. - [x] (Recommended) I joined the CommitPulse Discord server.
1 parent 74c5188 commit 33e9777

2 files changed

Lines changed: 24 additions & 5 deletions

File tree

utils/tracking.test.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,24 @@ describe('trackUser', () => {
114114

115115
expect(fetchMock).toHaveBeenCalledTimes(1);
116116
});
117+
118+
it('handles non-serializable input gracefully without throwing', () => {
119+
// A circular reference cannot be serialized by JSON.stringify and will throw
120+
// a TypeError. This test verifies that the utility does not propagate the
121+
// exception to the caller.
122+
const circular: Record<string, unknown> = {};
123+
circular['self'] = circular;
124+
125+
const originalStringify = JSON.stringify;
126+
vi.spyOn(JSON, 'stringify').mockImplementationOnce(() => {
127+
throw new TypeError('Converting circular structure to JSON');
128+
});
129+
130+
expect(() => trackUser('testuser')).not.toThrow();
131+
132+
JSON.stringify = originalStringify;
133+
});
134+
117135
it('does not run in SSR context when window is undefined', () => {
118136
const originalWindow = globalThis.window;
119137
const sendBeaconMock = vi.fn();
@@ -128,14 +146,10 @@ describe('trackUser', () => {
128146
value: sendBeaconMock,
129147
configurable: true,
130148
});
131-
132149
vi.stubGlobal('fetch', fetchMock);
133-
134150
trackUser('octocat');
135-
136151
expect(sendBeaconMock).not.toHaveBeenCalled();
137152
expect(fetchMock).not.toHaveBeenCalled();
138-
139153
Object.defineProperty(globalThis, 'window', {
140154
value: originalWindow,
141155
configurable: true,

utils/tracking.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ export function trackUser(username: string) {
22
if (typeof navigator === 'undefined' || typeof window === 'undefined') return;
33
if (!username) return;
44

5-
const payload = JSON.stringify({ username });
5+
let payload: string;
6+
try {
7+
payload = JSON.stringify({ username });
8+
} catch {
9+
return;
10+
}
611

712
const beaconQueued = navigator.sendBeacon
813
? navigator.sendBeacon('/api/track-user', new Blob([payload], { type: 'application/json' }))

0 commit comments

Comments
 (0)