fiddy/apps/web/__tests__/rate-limit.test.ts

102 lines
3.0 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import path from "node:path";
import { fileURLToPath } from "node:url";
import dotenv from "dotenv";
import getPool from "../lib/server/db";
import { ApiError } from "../lib/server/errors";
import { enforceAuthRateLimit, enforceUserWriteRateLimit } from "../lib/server/rate-limit";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const envLoaded = dotenv.config({ path: path.resolve(__dirname, "../../../.env") });
const hasDb = Boolean(process.env.DATABASE_URL);
async function ensureRateLimitTable() {
const pool = getPool();
await pool.query(`
create table if not exists rate_limits(
key text primary key,
window_start timestamptz not null,
count integer not null default 0,
updated_at timestamptz not null default now()
)
`);
}
test("auth rate limit blocks when threshold is exceeded", async t => {
if (!hasDb) {
t.skip("DATABASE_URL not set");
return;
}
if (envLoaded.error) t.diagnostic(String(envLoaded.error));
await ensureRateLimitTable();
const pool = getPool();
const marker = Date.now();
const ip = `test-ip-${marker}`;
const identifier = `rate_limit_${marker}@example.com`;
try {
await enforceAuthRateLimit({
route: "login",
ip,
identifier,
ipLimit: 1,
identifierLimit: 1,
windowMs: 60_000
});
await assert.rejects(
() => enforceAuthRateLimit({
route: "login",
ip,
identifier,
ipLimit: 1,
identifierLimit: 1,
windowMs: 60_000
}),
(error: unknown) => error instanceof ApiError && error.code === "RATE_LIMITED"
);
} finally {
await pool.query("delete from rate_limits where key like $1 or key like $2", [
`auth:login:ip:${ip}%`,
`auth:login:identifier:${identifier}%`
]);
}
});
test("user write rate limit blocks when threshold is exceeded", async t => {
if (!hasDb) {
t.skip("DATABASE_URL not set");
return;
}
if (envLoaded.error) t.diagnostic(String(envLoaded.error));
await ensureRateLimitTable();
const pool = getPool();
const userId = 987654;
const scope = `test_scope_${Date.now()}`;
try {
await enforceUserWriteRateLimit({
userId,
scope,
limit: 1,
windowMs: 60_000
});
await assert.rejects(
() => enforceUserWriteRateLimit({
userId,
scope,
limit: 1,
windowMs: 60_000
}),
(error: unknown) => error instanceof ApiError && error.code === "RATE_LIMITED"
);
} finally {
await pool.query("delete from rate_limits where key = $1", [
`write:user:${userId}:scope:${scope}`
]);
}
});