mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 10:52:17 +03:00
Two shards on release/v3.8.51 went red in one day with the same signature —
"ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from
combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only
.github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass
alone and on re-run: the cleanup races something still writing into the directory
(SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner
the window opens. 1154 test files do their own cleanup with
fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries.
One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record):
every rm / rmSync / rmdirSync option object with `recursive: true` and no
`maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries
ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292
files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included.
Only the option object changes: no call site, assertion or import is touched.
Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292
files; a random 20-file sample runs green (quota-redis-store hangs identically on
the untouched tree — it needs a Redis on localhost, an environment matter). The
four unit shards on this PR are the full run.
437 lines
12 KiB
TypeScript
437 lines
12 KiB
TypeScript
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-api-key-usage-limits-"));
|
|
process.env.DATA_DIR = TEST_DATA_DIR;
|
|
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "usage-limit-test-secret";
|
|
|
|
const core = await import("../../src/lib/db/core.ts");
|
|
const localDb = await import("../../src/lib/localDb.ts");
|
|
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
|
|
const usageHistory = await import("../../src/lib/usage/usageHistory.ts");
|
|
const usageLimits = await import("../../src/lib/usage/apiKeyUsageLimits.ts");
|
|
|
|
const NOW = Date.parse("2026-06-19T20:00:00.000Z");
|
|
|
|
async function resetStorage() {
|
|
core.resetDbInstance();
|
|
apiKeysDb.resetApiKeyState();
|
|
usageHistory.clearPendingRequests();
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
|
}
|
|
|
|
test.beforeEach(async () => {
|
|
await resetStorage();
|
|
});
|
|
|
|
test.after(() => {
|
|
core.resetDbInstance();
|
|
apiKeysDb.resetApiKeyState();
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
});
|
|
|
|
test("API key USD usage limits persist and default off", async () => {
|
|
const created = await apiKeysDb.createApiKey("Usage Limit Key", "machine-limit-01");
|
|
|
|
let metadata = await apiKeysDb.getApiKeyMetadata(created.key);
|
|
assert.equal(metadata?.usageLimitEnabled, false);
|
|
assert.equal(metadata?.dailyUsageLimitUsd, null);
|
|
assert.equal(metadata?.weeklyUsageLimitUsd, null);
|
|
|
|
await apiKeysDb.updateApiKeyPermissions(created.id, {
|
|
usageLimitEnabled: true,
|
|
dailyUsageLimitUsd: 10.5,
|
|
weeklyUsageLimitUsd: 50,
|
|
});
|
|
apiKeysDb.clearApiKeyCaches();
|
|
|
|
metadata = await apiKeysDb.getApiKeyMetadata(created.key);
|
|
assert.equal(metadata?.usageLimitEnabled, true);
|
|
assert.equal(metadata?.dailyUsageLimitUsd, 10.5);
|
|
assert.equal(metadata?.weeklyUsageLimitUsd, 50);
|
|
});
|
|
|
|
test("getApiKeyUsageLimitStatus aligns weekly USD spend with provider resetAt when available", async () => {
|
|
await localDb.updatePricing({
|
|
claude: {
|
|
"claude-opus-4-8": {
|
|
input: 1,
|
|
cached: 1,
|
|
output: 1,
|
|
reasoning: 1,
|
|
cache_creation: 1,
|
|
},
|
|
},
|
|
});
|
|
|
|
const created = await apiKeysDb.createApiKey("Metered Key", "machine-limit-02");
|
|
await apiKeysDb.updateApiKeyPermissions(created.id, {
|
|
usageLimitEnabled: true,
|
|
dailyUsageLimitUsd: 10,
|
|
weeklyUsageLimitUsd: 20,
|
|
});
|
|
|
|
await usageHistory.saveRequestUsage({
|
|
provider: "claude",
|
|
model: "claude-opus-4-8",
|
|
apiKeyId: created.id,
|
|
apiKeyName: "Metered Key",
|
|
tokens: { input: 2_000_000, output: 0 },
|
|
success: true,
|
|
timestamp: "2026-06-19T12:00:00.000Z",
|
|
});
|
|
await usageHistory.saveRequestUsage({
|
|
provider: "claude",
|
|
model: "claude-opus-4-8",
|
|
apiKeyId: created.id,
|
|
apiKeyName: "Metered Key",
|
|
tokens: { input: 3_000_000, output: 0 },
|
|
success: true,
|
|
timestamp: "2026-06-18T21:00:00.000Z",
|
|
});
|
|
await usageHistory.saveRequestUsage({
|
|
provider: "claude",
|
|
model: "claude-opus-4-8",
|
|
apiKeyId: created.id,
|
|
apiKeyName: "Metered Key",
|
|
tokens: { input: 7_000_000, output: 0 },
|
|
success: true,
|
|
timestamp: "2026-06-18T12:00:00.000Z",
|
|
});
|
|
|
|
const metadata = await apiKeysDb.getApiKeyMetadata(created.key);
|
|
assert.ok(metadata);
|
|
|
|
const weeklyResetAt = "2026-06-25T20:00:00.000Z";
|
|
const status = await usageLimits.getApiKeyUsageLimitStatus(
|
|
{ ...metadata, allowedConnections: ["conn-claude"] },
|
|
{
|
|
now: () => NOW,
|
|
getProviderConnectionById: async () => ({
|
|
id: "conn-claude",
|
|
provider: "claude",
|
|
isActive: true,
|
|
}),
|
|
getProviderConnections: async () => [],
|
|
getProviderLimitsCache: () => ({
|
|
plan: "Claude Max",
|
|
quotas: {
|
|
"weekly (7d)": {
|
|
used: 27,
|
|
total: 100,
|
|
resetAt: weeklyResetAt,
|
|
},
|
|
},
|
|
message: null,
|
|
fetchedAt: new Date(NOW).toISOString(),
|
|
}),
|
|
getAllProviderLimitsCache: () => ({}),
|
|
}
|
|
);
|
|
|
|
assert.equal(status.enabled, true);
|
|
assert.equal(status.dailySpentUsd, 2);
|
|
assert.equal(status.weeklySpentUsd, 5);
|
|
assert.equal(status.dailyLimitUsd, 10);
|
|
assert.equal(status.weeklyLimitUsd, 20);
|
|
assert.equal(status.dailyResetAtIso, "2026-06-20T03:00:00.000Z");
|
|
assert.equal(status.weeklyWindowStartIso, "2026-06-18T20:00:00.000Z");
|
|
assert.equal(status.weeklyResetAtIso, weeklyResetAt);
|
|
assert.equal(status.dailyExceeded, false);
|
|
assert.equal(status.weeklyExceeded, false);
|
|
});
|
|
|
|
test("getApiKeyUsageLimitStatus cuts weekly USD spend at observed provider quota reset", async () => {
|
|
await localDb.updatePricing({
|
|
claude: {
|
|
"claude-opus-4-8": {
|
|
input: 1,
|
|
cached: 1,
|
|
output: 1,
|
|
reasoning: 1,
|
|
cache_creation: 1,
|
|
},
|
|
},
|
|
});
|
|
|
|
const created = await apiKeysDb.createApiKey("Reset Cut Key", "machine-limit-reset");
|
|
await apiKeysDb.updateApiKeyPermissions(created.id, {
|
|
usageLimitEnabled: true,
|
|
dailyUsageLimitUsd: 10,
|
|
weeklyUsageLimitUsd: 20,
|
|
});
|
|
|
|
await usageHistory.saveRequestUsage({
|
|
provider: "claude",
|
|
model: "claude-opus-4-8",
|
|
apiKeyId: created.id,
|
|
apiKeyName: "Reset Cut Key",
|
|
tokens: { input: 7_000_000, output: 0 },
|
|
success: true,
|
|
timestamp: "2026-06-19T23:30:00.000Z",
|
|
});
|
|
await usageHistory.saveRequestUsage({
|
|
provider: "claude",
|
|
model: "claude-opus-4-8",
|
|
apiKeyId: created.id,
|
|
apiKeyName: "Reset Cut Key",
|
|
tokens: { input: 2_000_000, output: 0 },
|
|
success: true,
|
|
timestamp: "2026-06-20T02:00:00.000Z",
|
|
});
|
|
|
|
const db = core.getDbInstance();
|
|
const insertSnapshot = db.prepare(`
|
|
INSERT INTO quota_snapshots (
|
|
provider,
|
|
connection_id,
|
|
window_key,
|
|
remaining_percentage,
|
|
is_exhausted,
|
|
next_reset_at,
|
|
window_duration_ms,
|
|
raw_data,
|
|
created_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`);
|
|
insertSnapshot.run(
|
|
"claude",
|
|
"conn-claude",
|
|
"weekly (7d)",
|
|
72,
|
|
0,
|
|
"2026-06-25T23:00:00.000Z",
|
|
null,
|
|
null,
|
|
"2026-06-19T23:55:00.000Z"
|
|
);
|
|
insertSnapshot.run(
|
|
"claude",
|
|
"conn-claude",
|
|
"weekly (7d)",
|
|
100,
|
|
0,
|
|
"2026-06-25T23:00:00.000Z",
|
|
null,
|
|
null,
|
|
"2026-06-20T01:42:52.590Z"
|
|
);
|
|
insertSnapshot.run(
|
|
"claude",
|
|
"conn-claude",
|
|
"weekly (7d)",
|
|
99,
|
|
0,
|
|
"2026-06-25T23:00:00.000Z",
|
|
null,
|
|
null,
|
|
"2026-06-20T02:10:00.000Z"
|
|
);
|
|
db.prepare(
|
|
`
|
|
INSERT INTO provider_quota_reset_events
|
|
(provider, connection_id, window_key, window_started_at, window_resets_at,
|
|
observed_at, previous_remaining_percentage, new_remaining_percentage,
|
|
previous_used_percentage, new_used_percentage, raw_data)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`
|
|
).run(
|
|
"claude",
|
|
"conn-claude",
|
|
"weekly (7d)",
|
|
"2026-06-18T23:00:00.000Z",
|
|
"2026-06-25T23:00:00.000Z",
|
|
"2026-06-18T23:04:00.000Z",
|
|
0,
|
|
100,
|
|
100,
|
|
0,
|
|
null
|
|
);
|
|
|
|
const metadata = await apiKeysDb.getApiKeyMetadata(created.key);
|
|
assert.ok(metadata);
|
|
|
|
const weeklyResetAt = "2026-06-25T23:00:00.000Z";
|
|
const status = await usageLimits.getApiKeyUsageLimitStatus(
|
|
{ ...metadata, allowedConnections: ["conn-claude"] },
|
|
{
|
|
now: () => Date.parse("2026-06-20T14:30:00.000Z"),
|
|
getProviderConnectionById: async () => ({
|
|
id: "conn-claude",
|
|
provider: "claude",
|
|
isActive: true,
|
|
}),
|
|
getProviderConnections: async () => [],
|
|
getProviderLimitsCache: () => ({
|
|
plan: "Claude Max",
|
|
quotas: {
|
|
"weekly (7d)": {
|
|
used: 5,
|
|
total: 100,
|
|
resetAt: weeklyResetAt,
|
|
},
|
|
},
|
|
message: null,
|
|
fetchedAt: "2026-06-20T14:30:00.000Z",
|
|
}),
|
|
getAllProviderLimitsCache: () => ({}),
|
|
}
|
|
);
|
|
|
|
assert.equal(status.weeklyWindowStartIso, "2026-06-20T01:42:52.590Z");
|
|
assert.equal(status.weeklySpentUsd, 2);
|
|
});
|
|
|
|
test("buildApiKeyUsageLimitText returns API-key quota spend percentage and reset lines", async () => {
|
|
const text = usageLimits.buildApiKeyUsageLimitText(
|
|
{
|
|
enabled: true,
|
|
dailyLimitUsd: 10,
|
|
weeklyLimitUsd: 50,
|
|
dailySpentUsd: 2,
|
|
weeklySpentUsd: 5.25,
|
|
dailyWindowStartIso: "2026-06-19T03:00:00.000Z",
|
|
dailyResetAtIso: "2026-06-20T03:00:00.000Z",
|
|
weeklyWindowStartIso: "2026-06-12T20:00:00.000Z",
|
|
weeklyResetAtIso: "2026-06-25T20:00:00.000Z",
|
|
dailyExceeded: false,
|
|
weeklyExceeded: false,
|
|
},
|
|
Date.parse("2026-06-19T20:00:00.000Z")
|
|
);
|
|
|
|
assert.equal(
|
|
text,
|
|
[
|
|
"Daily quota",
|
|
"$10.00",
|
|
"Daily spent",
|
|
"$2.00",
|
|
"Daily used",
|
|
"20%",
|
|
"Resets in 7h 0m",
|
|
"",
|
|
"Weekly quota",
|
|
"$50.00",
|
|
"Weekly spent",
|
|
"$5.25",
|
|
"Weekly used",
|
|
"11%",
|
|
"Resets in 6d 0h 0m",
|
|
].join("\n")
|
|
);
|
|
});
|
|
|
|
test("buildApiKeyUsageLimitPercentText returns remaining percentages only", () => {
|
|
const text = usageLimits.buildApiKeyUsageLimitPercentText(
|
|
{
|
|
enabled: true,
|
|
dailyLimitUsd: 10,
|
|
weeklyLimitUsd: 50,
|
|
dailySpentUsd: 2,
|
|
weeklySpentUsd: 5.25,
|
|
dailyWindowStartIso: "2026-06-19T03:00:00.000Z",
|
|
dailyResetAtIso: "2026-06-20T03:00:00.000Z",
|
|
weeklyWindowStartIso: "2026-06-12T20:00:00.000Z",
|
|
weeklyResetAtIso: "2026-06-25T20:00:00.000Z",
|
|
dailyExceeded: false,
|
|
weeklyExceeded: false,
|
|
},
|
|
Date.parse("2026-06-19T20:00:00.000Z")
|
|
);
|
|
|
|
assert.equal(
|
|
text,
|
|
["Daily", "80% left", "⏱ reset in 7h 0m", "", "Weekly", "90% left", "⏱ reset in 6d 0h 0m"].join(
|
|
"\n"
|
|
)
|
|
);
|
|
});
|
|
|
|
test("buildApiKeyUsageLimitRejection includes over-quota percentage and reset hint", async () => {
|
|
const response = usageLimits.buildApiKeyUsageLimitRejection(
|
|
new Request("http://localhost/v1/messages", {
|
|
headers: { "anthropic-version": "2023-06-01" },
|
|
}),
|
|
{
|
|
enabled: true,
|
|
dailyLimitUsd: 10,
|
|
weeklyLimitUsd: 1,
|
|
dailySpentUsd: 0.25,
|
|
weeklySpentUsd: 1.09,
|
|
dailyWindowStartIso: "2026-06-19T03:00:00.000Z",
|
|
dailyResetAtIso: "2026-06-20T03:00:00.000Z",
|
|
weeklyWindowStartIso: "2026-06-12T20:00:00.000Z",
|
|
weeklyResetAtIso: "2026-06-25T20:00:00.000Z",
|
|
dailyExceeded: false,
|
|
weeklyExceeded: true,
|
|
},
|
|
Date.parse("2026-06-19T20:00:00.000Z")
|
|
);
|
|
|
|
assert.equal(response.status, 400);
|
|
const body = (await response.json()) as { error: { message: string } };
|
|
assert.equal(
|
|
body.error.message,
|
|
"This API key reached its weekly USD usage quota ($1.09 of $1.00, 109%). Resets in 6d 0h 0m. Choose another allowed model after reset."
|
|
);
|
|
});
|
|
|
|
test("buildApiKeyUsageLimitRejection can hide USD amounts for client-facing policy errors", async () => {
|
|
const response = usageLimits.buildApiKeyUsageLimitRejection(
|
|
new Request("http://localhost/v1/messages", {
|
|
headers: { "anthropic-version": "2023-06-01" },
|
|
}),
|
|
{
|
|
enabled: true,
|
|
dailyLimitUsd: 10,
|
|
weeklyLimitUsd: 1,
|
|
dailySpentUsd: 0.25,
|
|
weeklySpentUsd: 1.09,
|
|
dailyWindowStartIso: "2026-06-19T03:00:00.000Z",
|
|
dailyResetAtIso: "2026-06-20T03:00:00.000Z",
|
|
weeklyWindowStartIso: "2026-06-12T20:00:00.000Z",
|
|
weeklyResetAtIso: "2026-06-25T20:00:00.000Z",
|
|
dailyExceeded: false,
|
|
weeklyExceeded: true,
|
|
},
|
|
Date.parse("2026-06-19T20:00:00.000Z"),
|
|
{ showUsd: false }
|
|
);
|
|
|
|
assert.equal(response.status, 400);
|
|
const body = (await response.json()) as { error: { message: string } };
|
|
assert.equal(
|
|
body.error.message,
|
|
"This API key reached its weekly usage quota (109%). Resets in 6d 0h 0m. Choose another allowed model after reset."
|
|
);
|
|
});
|
|
|
|
test("buildApiKeyUsageLimitRejection uses 400 so Claude Code does not trigger login", () => {
|
|
const response = usageLimits.buildApiKeyUsageLimitRejection(
|
|
new Request("http://localhost/v1/messages", {
|
|
headers: { "anthropic-version": "2023-06-01" },
|
|
}),
|
|
{
|
|
enabled: true,
|
|
dailyLimitUsd: 10,
|
|
weeklyLimitUsd: 50,
|
|
dailySpentUsd: 12,
|
|
weeklySpentUsd: 20,
|
|
dailyWindowStartIso: "2026-06-19T03:00:00.000Z",
|
|
dailyResetAtIso: "2026-06-20T03:00:00.000Z",
|
|
weeklyWindowStartIso: "2026-06-12T20:00:00.000Z",
|
|
weeklyResetAtIso: "2026-06-19T20:00:00.000Z",
|
|
dailyExceeded: true,
|
|
weeklyExceeded: false,
|
|
}
|
|
);
|
|
|
|
assert.equal(response.status, 400);
|
|
});
|