Files
ecommerce-platform/test/unit/lib/rate-limit.test.ts
T
davide 5428eeccc1 test: add unit tests for lib/ (validate, auth, storage, email, rate-limit)
49 tests for all 10 Zod schemas in validate.ts, 26 tests for auth (hashPassword,
verifyPassword, createSession, getSession, getCurrentUser, deleteSession), 11 for
storage (magic-byte validation, saveImage, deleteImageFile), 9 for email (sendMail
scenarios), and 6 for rate limiting logic.
2026-05-19 14:07:38 +02:00

72 lines
2.5 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import '../../../test/__mocks__/prisma'
import { checkRateLimit, recordAttempt } from '@/lib/rate-limit'
import { prisma } from '@/lib/prisma'
beforeEach(() => {
vi.clearAllMocks()
})
describe('checkRateLimit', () => {
it('returns not limited when attempts < 10', async () => {
vi.mocked(prisma.loginAttempt.count).mockResolvedValue(5)
const result = await checkRateLimit('1.2.3.4')
expect(result.limited).toBe(false)
expect(result.remaining).toBe(5)
})
it('returns limited when attempts >= 10', async () => {
vi.mocked(prisma.loginAttempt.count).mockResolvedValue(10)
const result = await checkRateLimit('1.2.3.4')
expect(result.limited).toBe(true)
expect(result.remaining).toBe(0)
})
it('returns limited when attempts > 10', async () => {
vi.mocked(prisma.loginAttempt.count).mockResolvedValue(15)
const result = await checkRateLimit('1.2.3.4')
expect(result.limited).toBe(true)
})
it('returns remaining = 1 when attempts = 9', async () => {
vi.mocked(prisma.loginAttempt.count).mockResolvedValue(9)
const result = await checkRateLimit('1.2.3.4')
expect(result.limited).toBe(false)
expect(result.remaining).toBe(1)
})
it('queries with a windowStart within the last 15 minutes', async () => {
vi.mocked(prisma.loginAttempt.count).mockResolvedValue(0)
const before = new Date(Date.now() - 15 * 60 * 1000 - 100)
await checkRateLimit('1.2.3.4')
const callArg = vi.mocked(prisma.loginAttempt.count).mock.calls[0][0]
const windowStart = callArg?.where?.createdAt?.gte as Date
expect(windowStart.getTime()).toBeGreaterThan(before.getTime())
})
})
describe('recordAttempt', () => {
it('creates a login attempt record', async () => {
vi.mocked(prisma.loginAttempt.create).mockResolvedValue({ id: '1', key: '1.2.3.4', createdAt: new Date() })
vi.mocked(prisma.loginAttempt.deleteMany).mockResolvedValue({ count: 0 })
await recordAttempt('1.2.3.4')
expect(prisma.loginAttempt.create).toHaveBeenCalledWith({ data: { key: '1.2.3.4' } })
})
it('cleans up old records after creating', async () => {
vi.mocked(prisma.loginAttempt.create).mockResolvedValue({ id: '1', key: '1.2.3.4', createdAt: new Date() })
vi.mocked(prisma.loginAttempt.deleteMany).mockResolvedValue({ count: 0 })
await recordAttempt('1.2.3.4')
expect(prisma.loginAttempt.deleteMany).toHaveBeenCalledWith(
expect.objectContaining({ where: expect.objectContaining({ createdAt: expect.anything() }) })
)
})
})