import assert from "node:assert/strict"; import { dbRun, withTransaction } from "../server/db.mjs"; const api = process.env.AI_DRAMA_API_BASE || "http://127.0.0.1:8787"; const scope = { "x-organization-id": "org-studio-lab", "x-workspace-id": "ws-local-aidrama", "x-project-id": "thunder-mouth" }; async function request(path, headers = {}, options = {}) { const response = await fetch(`${api}${path}`, { ...options, headers: { "content-type": "application/json", ...headers, ...(options.headers || {}) } }); const payload = await response.json().catch(() => ({})); return { response, payload }; } async function login() { const result = await request("/api/auth/login", {}, { method: "POST", body: JSON.stringify({ email: "producer@local.test", password: "Demo@123456" }) }); assert.equal(result.response.ok, true, "限流回归登录失败"); return { authorization: `Bearer ${result.payload.session.token}` }; } const owner = { ...(await login()), ...scope }; const restoreOwner = { ...(await login()), ...scope }; let clientId = ""; let originalLimit = 120; let changedLimit = false; try { const config = await request("/api/system/config", owner); assert.equal(config.response.ok, true, "读取系统配置失败"); const setting = config.payload.settings.find((item) => item.key === "api.rate_limit_per_minute"); originalLimit = Number(setting?.value ?? 120); const created = await request("/api/system/api-clients", owner, { method: "POST", body: JSON.stringify({ name: `Smoke Rate Limit Client ${Date.now()}`, scopes: ["jobs:read"] }) }); assert.equal(created.response.status, 201, "限流回归 API 客户端创建失败"); clientId = created.payload.client.id; const clientHeaders = { authorization: `Bearer ${created.payload.clientKey}`, ...scope }; const updated = await request("/api/system/config", owner, { method: "POST", body: JSON.stringify({ settings: [{ key: "api.rate_limit_per_minute", value: 2 }] }) }); assert.equal(updated.response.ok, true, "降低 API 限流配置失败"); changedLimit = true; const first = await request("/api/jobs", clientHeaders); const second = await request("/api/jobs", clientHeaders); const third = await request("/api/jobs", clientHeaders); assert.equal(first.response.status, 200, "限流窗口内第一次请求不应被阻断"); assert.equal(second.response.status, 200, "限流窗口内第二次请求不应被阻断"); assert.equal(first.response.headers.get("x-ratelimit-limit"), "2", "响应必须返回限流上限"); assert.equal(second.response.headers.get("x-ratelimit-remaining"), "0", "第二次请求后剩余额度应为 0"); assert.equal(third.response.status, 429, "超过限流上限必须返回 429"); assert.equal(third.payload.error, "rate_limit_exceeded", "限流错误码必须稳定"); assert.ok(Number(third.response.headers.get("retry-after")) > 0, "429 必须返回 Retry-After"); assert.equal(third.response.headers.get("x-ratelimit-remaining"), "0", "429 响应剩余额度应为 0"); console.log("api rate-limit smoke passed: configured limit, headers, 429, retry-after"); } finally { if (changedLimit) { const restored = await request("/api/system/config", restoreOwner, { method: "POST", body: JSON.stringify({ settings: [{ key: "api.rate_limit_per_minute", value: originalLimit }] }) }); assert.equal(restored.response.ok, true, "限流回归未能恢复原配置"); } if (clientId) { withTransaction(() => { dbRun("DELETE FROM api_clients WHERE id = ?", [clientId]); dbRun("DELETE FROM audit_logs WHERE target_id = ?", [clientId]); }); } }