From 210065166a763fd9080592d9bfd3ef234b67f282 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 06:57:15 +0000 Subject: [PATCH 1/2] test: add tests for handleRateLimit in apiUtils Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com> --- src/lib/__tests__/apiUtils.test.ts | 68 +++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/lib/__tests__/apiUtils.test.ts b/src/lib/__tests__/apiUtils.test.ts index fbd65ba..ff20b08 100644 --- a/src/lib/__tests__/apiUtils.test.ts +++ b/src/lib/__tests__/apiUtils.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { handleErrorResponse, getAuthenticatedUser } from '../apiUtils'; +import { handleErrorResponse, handleRateLimit, getAuthenticatedUser } from '../apiUtils'; +import { RateLimitError } from '@/lib/types'; import { NextResponse } from 'next/server'; import { getServerSession } from "next-auth"; @@ -56,6 +57,71 @@ describe('apiUtils', () => { }); }); + + describe('handleRateLimit', () => { + let originalDateNow; + const mockNow = 1700000000000; // 2023-11-14T22:13:20.000Z + + beforeEach(() => { + originalDateNow = Date.now; + Date.now = vi.fn(() => mockNow); + }); + + afterEach(() => { + Date.now = originalDateNow; + }); + + it('should throw RateLimitError with X-RateLimit-Reset header timestamp', () => { + const resetTimestamp = 1700003600; + const res = new Response(null, { + headers: { + 'X-RateLimit-Reset': resetTimestamp.toString(), + }, + }); + + expect(() => handleRateLimit(res)).toThrow(RateLimitError); + + try { + handleRateLimit(res); + } catch (error) { + expect(error).toBeInstanceOf(RateLimitError); + expect(error.resetAt.getTime()).toBe(resetTimestamp * 1000); + } + }); + + it('should throw RateLimitError with default timestamp (1 hour from now) if header is missing', () => { + const res = new Response(null); + + expect(() => handleRateLimit(res)).toThrow(RateLimitError); + + try { + handleRateLimit(res); + } catch (error) { + expect(error).toBeInstanceOf(RateLimitError); + const expectedResetTimestamp = Math.floor(mockNow / 1000) + 3600; + expect(error.resetAt.getTime()).toBe(expectedResetTimestamp * 1000); + } + }); + + it('should throw RateLimitError with default timestamp if header is invalid', () => { + const res = new Response(null, { + headers: { + 'X-RateLimit-Reset': 'invalid-timestamp', + }, + }); + + expect(() => handleRateLimit(res)).toThrow(RateLimitError); + + try { + handleRateLimit(res); + } catch (error) { + expect(error).toBeInstanceOf(RateLimitError); + const expectedResetTimestamp = Math.floor(mockNow / 1000) + 3600; + expect(error.resetAt.getTime()).toBe(expectedResetTimestamp * 1000); + } + }); + }); + describe('getAuthenticatedUser', () => { it('should return user object if session is valid', async () => { vi.mocked(getServerSession).mockResolvedValueOnce({ From d9ef1a2737dd0a47d08914b26d8a1ba86547e7fd Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 07:15:46 +0000 Subject: [PATCH 2/2] fix: resolve typescript errors in apiUtils test Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com> --- src/lib/__tests__/apiUtils.test.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib/__tests__/apiUtils.test.ts b/src/lib/__tests__/apiUtils.test.ts index ff20b08..49bd0b8 100644 --- a/src/lib/__tests__/apiUtils.test.ts +++ b/src/lib/__tests__/apiUtils.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { handleErrorResponse, handleRateLimit, getAuthenticatedUser } from '../apiUtils'; import { RateLimitError } from '@/lib/types'; import { NextResponse } from 'next/server'; @@ -59,7 +59,7 @@ describe('apiUtils', () => { describe('handleRateLimit', () => { - let originalDateNow; + let originalDateNow: () => number; const mockNow = 1700000000000; // 2023-11-14T22:13:20.000Z beforeEach(() => { @@ -83,7 +83,8 @@ describe('apiUtils', () => { try { handleRateLimit(res); - } catch (error) { + } // eslint-disable-next-line @typescript-eslint/no-explicit-any + catch (error: any) { expect(error).toBeInstanceOf(RateLimitError); expect(error.resetAt.getTime()).toBe(resetTimestamp * 1000); } @@ -96,7 +97,8 @@ describe('apiUtils', () => { try { handleRateLimit(res); - } catch (error) { + } // eslint-disable-next-line @typescript-eslint/no-explicit-any + catch (error: any) { expect(error).toBeInstanceOf(RateLimitError); const expectedResetTimestamp = Math.floor(mockNow / 1000) + 3600; expect(error.resetAt.getTime()).toBe(expectedResetTimestamp * 1000); @@ -114,7 +116,8 @@ describe('apiUtils', () => { try { handleRateLimit(res); - } catch (error) { + } // eslint-disable-next-line @typescript-eslint/no-explicit-any + catch (error: any) { expect(error).toBeInstanceOf(RateLimitError); const expectedResetTimestamp = Math.floor(mockNow / 1000) + 3600; expect(error.resetAt.getTime()).toBe(expectedResetTimestamp * 1000);