From 87669bce28e45ed0c26bdd1f5fdb6327ab706bdd Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 06:34:16 +0000 Subject: [PATCH] test: add tests for handleRateLimit utility Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com> --- src/lib/__tests__/apiUtils.test.ts | 63 +++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/src/lib/__tests__/apiUtils.test.ts b/src/lib/__tests__/apiUtils.test.ts index fbd65baa..2885ce9f 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 { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { handleErrorResponse, getAuthenticatedUser, handleRateLimit } from '../apiUtils'; +import { RateLimitError } from '../types'; import { NextResponse } from 'next/server'; import { getServerSession } from "next-auth"; @@ -56,6 +57,64 @@ describe('apiUtils', () => { }); }); + + describe('handleRateLimit', () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it('should throw RateLimitError using timestamp from X-RateLimit-Reset header', () => { + const resetTimestamp = Math.floor(Date.now() / 1000) + 1000; + const res = new Response(null, { + headers: { 'X-RateLimit-Reset': resetTimestamp.toString() } + }); + + try { + handleRateLimit(res); + expect.fail('Should have thrown RateLimitError'); + } catch (error) { + expect(error).toBeInstanceOf(RateLimitError); + expect((error as RateLimitError).resetAt.getTime()).toBe(resetTimestamp * 1000); + } + }); + + it('should fall back to 1 hour from now if header is missing', () => { + vi.useFakeTimers(); + const now = new Date('2024-01-01T12:00:00Z'); + vi.setSystemTime(now); + + const res = new Response(null); + + try { + handleRateLimit(res); + expect.fail('Should have thrown RateLimitError'); + } catch (error) { + expect(error).toBeInstanceOf(RateLimitError); + const expectedResetTimestamp = Math.floor(now.getTime() / 1000) + 3600; + expect((error as RateLimitError).resetAt.getTime()).toBe(expectedResetTimestamp * 1000); + } + }); + + it('should fall back to 1 hour from now if header is invalid', () => { + vi.useFakeTimers(); + const now = new Date('2024-01-01T12:00:00Z'); + vi.setSystemTime(now); + + const res = new Response(null, { + headers: { 'X-RateLimit-Reset': 'invalid' } + }); + + try { + handleRateLimit(res); + expect.fail('Should have thrown RateLimitError'); + } catch (error) { + expect(error).toBeInstanceOf(RateLimitError); + const expectedResetTimestamp = Math.floor(now.getTime() / 1000) + 3600; + expect((error as RateLimitError).resetAt.getTime()).toBe(expectedResetTimestamp * 1000); + } + }); + }); + describe('getAuthenticatedUser', () => { it('should return user object if session is valid', async () => { vi.mocked(getServerSession).mockResolvedValueOnce({