Ctrl K
ComunidadGuías…

tests/api-key-rate-limit-window.unit.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import type { Context } from 'hono'
import { afterEach, describe, expect, it, vi } from 'vitest'

// In-memory stand-in for the Cloudflare cache that backs the rate limit counters.
const store = new Map<string, unknown>()
vi.mock('../supabase/functions/_backend/utils/cache.ts', () => ({
  CacheHelper: class {
    buildRequest(path: string, params: Record<string, string> = {}) {
      return new Request(`https://cache.local${path}?${new URLSearchParams(params)}`)
    }

    async matchJson<T>(key: Request): Promise<T | null> {
      return (store.get(key.url) as T | undefined) ?? null
    }

    async putJson(key: Request, payload: unknown) {
      store.set(key.url, payload)
    }
  },
}))

const { isAPIKeyRateLimited, isIPRateLimited, recordAPIKeyUsage, recordFailedAuth } = await import('../supabase/functions/_backend/utils/rate_limit.ts')

function context(): Context {
  return {
    env: {},
    get: () => 'test-request',
    req: { header: (name: string) => (name === 'cf-connecting-ip' ? '203.0.113.7' : undefined) },
  } as unknown as Context
}

afterEach(() => {
  store.clear()
  vi.unstubAllEnvs()
  vi.useRealTimers()
})

describe('API key rate limit window', () => {
  it('allows exactly the advertised quota per window', async () => {
    vi.stubEnv('RATE_LIMIT_API_KEY', '3')
    const c = context()
    const results: boolean[] = []
    for (let i = 0; i < 4; i++) {
      await recordAPIKeyUsage(c, 1)
      results.push((await isAPIKeyRateLimited(c, 1)).limited)
    }
    expect(results).toEqual([false, false, false, true])
  })

  it('keeps a fixed window: calls do not extend it, and the quota recovers when it ends', async () => {
    vi.stubEnv('RATE_LIMIT_API_KEY', '2')
    vi.useFakeTimers({ now: 1_000_000 })
    const c = context()
    const first = await recordAPIKeyUsage(c, 1)
    expect(first.resetAt).toBe(1_060_000)

    vi.setSystemTime(1_030_000)
    const second = await recordAPIKeyUsage(c, 1)
    expect(second).toEqual({ count: 2, resetAt: 1_060_000 })
    await recordAPIKeyUsage(c, 1)
    expect((await isAPIKeyRateLimited(c, 1)).limited).toBe(true)

    vi.setSystemTime(1_060_001)
    const next = await recordAPIKeyUsage(c, 1)
    expect(next).toEqual({ count: 1, resetAt: 1_120_001 })
    expect((await isAPIKeyRateLimited(c, 1)).limited).toBe(false)
  })
})

describe('failed-auth IP limit', () => {
  it('still blocks once the 20th failed attempt is recorded', async () => {
    const c = context()
    for (let i = 0; i < 19; i++)
      await recordFailedAuth(c)
    expect((await isIPRateLimited(c)).limited).toBe(false)
    await recordFailedAuth(c)
    expect((await isIPRateLimited(c)).limited).toBe(true)
  })
})