Files
OmniRoute/tests/unit/rate-limit-manager.test.mjs
diegosouzapw f1e30dfba2 fix(usage): guard GLM region lookup and stabilize test runs
Only use provider apiRegion values when they are strings before resolving
the GLM quota endpoint, preventing invalid metadata from affecting usage
requests.

Run unit tests with single-test concurrency to avoid shared-state flakes
and expand coverage for auth-protected routes, provider node validation,
proxy and stream handling, model sync, token refresh, and protobuf
parsing.
2026-04-06 11:15:44 -03:00

173 lines
5.5 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-rate-limit-manager-"));
process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const rateLimitManager = await import("../../open-sse/services/rateLimitManager.ts");
const accountFallback = await import("../../open-sse/services/accountFallback.ts");
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function flushBackgroundWork() {
await wait(50);
await new Promise((resolve) => setImmediate(resolve));
}
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.beforeEach(async () => {
await resetStorage();
});
test.afterEach(async () => {
await rateLimitManager.__resetRateLimitManagerForTests();
await flushBackgroundWork();
});
test.after(async () => {
await rateLimitManager.__resetRateLimitManagerForTests();
await flushBackgroundWork();
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("rate limit manager bypasses disabled connections and exposes inactive status", async () => {
const result = await rateLimitManager.withRateLimit("openai", "disabled-conn", null, async () => {
return "bypassed";
});
assert.equal(result, "bypassed");
assert.deepEqual(rateLimitManager.getRateLimitStatus("openai", "disabled-conn"), {
enabled: false,
active: false,
queued: 0,
running: 0,
});
assert.deepEqual(rateLimitManager.getAllRateLimitStatus(), {});
});
test("rate limit manager handles soft over-limit warnings and normal header learning", async () => {
rateLimitManager.enableRateLimitProtection("conn-over-limit");
rateLimitManager.updateFromHeaders(
"openai",
"conn-over-limit",
{ "x-ratelimit-over-limit": "yes" },
200
);
const softStatus = rateLimitManager.getRateLimitStatus("openai", "conn-over-limit");
assert.equal(softStatus.enabled, true);
assert.equal(softStatus.active, true);
rateLimitManager.enableRateLimitProtection("conn-low-remaining");
rateLimitManager.updateFromHeaders(
"openai",
"conn-low-remaining",
{
"x-ratelimit-limit-requests": "100",
"x-ratelimit-remaining-requests": "5",
"x-ratelimit-reset-requests": "30s",
},
200
);
await rateLimitManager.__flushLearnedLimitsForTests();
const learnedLimits = rateLimitManager.getLearnedLimits();
const learnedEntry = learnedLimits["openai:conn-low-remaining"];
assert.equal(learnedEntry.provider, "openai");
assert.equal(learnedEntry.connectionId, "conn-low-remaining");
assert.equal(learnedEntry.limit, 100);
assert.equal(learnedEntry.remaining, 5);
assert.ok(learnedEntry.minTime > 0);
rateLimitManager.enableRateLimitProtection("conn-high-remaining");
rateLimitManager.updateFromHeaders(
"claude",
"conn-high-remaining",
{
get(name) {
const map = {
"anthropic-ratelimit-requests-limit": "100",
"anthropic-ratelimit-requests-remaining": "70",
"anthropic-ratelimit-requests-reset": new Date(Date.now() + 30_000).toISOString(),
};
return map[name] ?? null;
},
},
200
);
await rateLimitManager.__flushLearnedLimitsForTests();
const allStatuses = rateLimitManager.getAllRateLimitStatus();
assert.ok(allStatuses["openai:conn-over-limit"]);
assert.ok(allStatuses["openai:conn-low-remaining"]);
assert.ok(allStatuses["claude:conn-high-remaining"]);
});
test("rate limit manager handles 429 limiter teardown and disable cleanup", async () => {
rateLimitManager.enableRateLimitProtection("conn-429");
rateLimitManager.updateFromHeaders("openai", "conn-429", { "retry-after": "1s" }, 429, "gpt-4o");
await wait(25);
assert.equal(rateLimitManager.getRateLimitStatus("openai", "conn-429").active, false);
rateLimitManager.enableRateLimitProtection("conn-disable");
rateLimitManager.updateFromHeaders(
"gemini",
"conn-disable",
{
"x-ratelimit-limit-requests": "60",
"x-ratelimit-remaining-requests": "4",
"x-ratelimit-reset-requests": "10s",
},
200,
"gemini-2.5-flash"
);
await rateLimitManager.__flushLearnedLimitsForTests();
assert.ok(rateLimitManager.getAllRateLimitStatus()["gemini:conn-disable:gemini-2.5-flash"]);
rateLimitManager.disableRateLimitProtection("conn-disable");
assert.equal(rateLimitManager.isRateLimitEnabled("conn-disable"), false);
assert.equal(rateLimitManager.getRateLimitStatus("gemini", "conn-disable").active, false);
});
test("rate limit manager parses retry hints from response bodies and locks models", async () => {
rateLimitManager.enableRateLimitProtection("conn-body");
rateLimitManager.updateFromResponseBody(
"openai",
"conn-body",
{
error: {
details: [{ retryDelay: "2s" }],
message: "Please retry later",
},
},
429,
"gpt-4o"
);
const lockout = accountFallback.getModelLockoutInfo("openai", "conn-body", "gpt-4o");
assert.equal(lockout.reason, "rate_limit_exceeded");
assert.ok(lockout.remainingMs > 0);
rateLimitManager.updateFromResponseBody(
"openai",
"conn-body",
JSON.stringify({ error: { type: "rate_limit_error" } }),
429,
null
);
assert.equal(rateLimitManager.getRateLimitStatus("openai", "conn-body").active, true);
});