Files
diegosouzapw--omniroute/tests/unit/api-key-self-service.test.ts
2026-07-13 13:39:12 +08:00

428 lines
14 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import DatabaseSync from "better-sqlite3";
import { SELF_ACCOUNT_QUOTA_SCOPE, SELF_USAGE_SCOPE } from "../../src/shared/constants/selfServiceScopes.ts";
import { buildApiKeySelfServiceStatus } from "../../src/lib/usage/apiKeySelfService.ts";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const migrationPath = path.join(
repoRoot,
"src/lib/db/migrations/075_api_key_self_service_usage_scopes.sql"
);
test("self-service scope migration backfills own usage once and preserves explicit account quota opt-in", () => {
const sql = fs.readFileSync(migrationPath, "utf8");
const db = new DatabaseSync(":memory:");
db.exec(`
CREATE TABLE api_keys (
id TEXT PRIMARY KEY,
scopes TEXT
);
INSERT INTO api_keys (id, scopes) VALUES
('legacy-empty', '[]'),
('legacy-null', NULL),
('custom', '["custom:scope"]'),
('quota-opt-in', '["${SELF_ACCOUNT_QUOTA_SCOPE}"]'),
('already-disabled-after-migration', '["custom:scope"]');
`);
db.exec(sql);
db.prepare("UPDATE api_keys SET scopes = ? WHERE id = ?").run(
JSON.stringify(["custom:scope"]),
"already-disabled-after-migration"
);
db.exec(sql);
const rows = db.prepare("SELECT id, scopes FROM api_keys ORDER BY id").all() as Array<{
id: string;
scopes: string;
}>;
const scopesById = new Map(rows.map((row) => [row.id, JSON.parse(row.scopes) as string[]]));
assert.deepEqual(scopesById.get("legacy-empty"), [SELF_USAGE_SCOPE]);
assert.deepEqual(scopesById.get("legacy-null"), [SELF_USAGE_SCOPE]);
assert.deepEqual(scopesById.get("custom"), ["custom:scope", SELF_USAGE_SCOPE]);
assert.deepEqual(scopesById.get("quota-opt-in"), [
SELF_ACCOUNT_QUOTA_SCOPE,
SELF_USAGE_SCOPE,
]);
assert.deepEqual(scopesById.get("already-disabled-after-migration"), ["custom:scope"]);
});
function makeDeps(overrides: Record<string, unknown> = {}) {
const tokenRows = overrides.tokenRows ?? {
inputTokens: 900,
outputTokens: 30,
cacheReadTokens: 120,
cacheCreationTokens: 10,
reasoningTokens: 5,
};
const dbParams: unknown[][] = [];
return {
dbParams,
deps: {
now: () => Date.UTC(2026, 4, 29, 12, 0, 0),
getCostSummary: () => ({
budget: null,
totalCostMonth: 12.34,
totalCostPeriod: 0,
activeLimitUsd: 0,
resetInterval: null,
resetTime: null,
budgetResetAt: null,
lastBudgetResetAt: null,
periodStartAt: null,
nextResetAt: null,
warningThreshold: null,
}),
checkBudget: () => ({ allowed: true }),
getDbInstance: () => ({
prepare: () => ({
get: (...params: unknown[]) => {
dbParams.push(params);
return tokenRows;
},
}),
}),
getProviderConnectionById: async () => null,
getProviderConnections: async () => [],
fetchAndPersistProviderLimits: async () => {
throw new Error("unexpected quota fetch");
},
...overrides,
},
};
}
test("self-service status reports own cost and token usage with null budget fields when no budget exists", async () => {
const metadata = {
id: "key-a",
name: "team-a",
scopes: [SELF_USAGE_SCOPE],
allowedConnections: [],
};
const { deps, dbParams } = makeDeps();
const status = await buildApiKeySelfServiceStatus(metadata, deps);
assert.deepEqual(status.apiKey, { id: "key-a", name: "team-a" });
assert.equal(status.usage.cost.usedUsd, 12.34);
assert.equal(status.usage.cost.limitUsd, null);
assert.equal(status.usage.cost.remainingUsd, null);
assert.equal(status.usage.cost.usedPercent, null);
assert.equal(status.usage.cost.period, "monthly");
assert.equal(status.usage.tokens.totalTokens, 1065);
assert.equal(dbParams[0][0], "key-a");
assert.equal(dbParams[0][1], "2026-05-01T00:00:00.000Z");
assert.equal("accountQuota" in status, false);
});
test("self-service status reports USD budget percentage using the budget period", async () => {
const metadata = {
id: "key-budget",
name: "budgeted",
scopes: [SELF_USAGE_SCOPE],
allowedConnections: [],
};
const periodStart = Date.UTC(2026, 4, 1, 0, 0, 0);
const nextReset = Date.UTC(2026, 5, 1, 0, 0, 0);
const { deps } = makeDeps({
getCostSummary: () => ({
budget: { resetInterval: "monthly" },
totalCostMonth: 99,
totalCostPeriod: 12.5,
activeLimitUsd: 50,
resetInterval: "monthly",
resetTime: "00:00",
budgetResetAt: nextReset,
lastBudgetResetAt: periodStart,
periodStartAt: periodStart,
nextResetAt: nextReset,
warningThreshold: 0.8,
}),
});
const status = await buildApiKeySelfServiceStatus(metadata, deps);
assert.equal(status.usage.cost.usedUsd, 12.5);
assert.equal(status.usage.cost.limitUsd, 50);
assert.equal(status.usage.cost.remainingUsd, 37.5);
assert.equal(status.usage.cost.usedPercent, 25);
assert.equal(status.usage.cost.periodStartAt, "2026-05-01T00:00:00.000Z");
assert.equal(status.usage.cost.resetAt, "2026-06-01T00:00:00.000Z");
});
test("self-service status preserves ISO and Date budget timestamps", async () => {
const metadata = {
id: "key-budget-date",
name: "budgeted date",
scopes: [SELF_USAGE_SCOPE],
allowedConnections: [],
};
const { deps, dbParams } = makeDeps({
getCostSummary: () => ({
budget: { resetInterval: "weekly" },
totalCostMonth: 99,
totalCostPeriod: 15,
activeLimitUsd: 60,
resetInterval: "weekly",
resetTime: "00:00",
budgetResetAt: null,
lastBudgetResetAt: null,
periodStartAt: "2026-05-18T00:00:00.000Z",
nextResetAt: new Date("2026-05-25T00:00:00.000Z"),
warningThreshold: 0.8,
}),
});
const status = await buildApiKeySelfServiceStatus(metadata, deps);
assert.equal(status.usage.cost.periodStartAt, "2026-05-18T00:00:00.000Z");
assert.equal(status.usage.cost.resetAt, "2026-05-25T00:00:00.000Z");
assert.equal(dbParams[0][1], "2026-05-18T00:00:00.000Z");
});
test("self-service status reports all explicitly allowed provider account quotas", async () => {
const metadata = {
id: "key-multi",
name: "multi",
scopes: [SELF_USAGE_SCOPE, SELF_ACCOUNT_QUOTA_SCOPE],
allowedConnections: ["conn-codex", "conn-claude"],
};
const fetches: string[] = [];
const { deps } = makeDeps({
getProviderConnectionById: async (connectionId: string) => ({
id: connectionId,
provider: connectionId === "conn-codex" ? "codex" : "claude",
isActive: true,
}),
fetchAndPersistProviderLimits: async (connectionId: string) => {
fetches.push(connectionId);
if (connectionId === "conn-codex") {
return {
connection: { id: connectionId, provider: "codex" },
usage: {
plan: "ChatGPT Plus",
quotas: {
session: { used: 1, remaining: 99, resetAt: "2026-05-29T18:11:44.000Z" },
},
},
cache: { quotas: null, plan: null, message: null, fetchedAt: "" },
};
}
return {
connection: { id: connectionId, provider: "claude" },
usage: {
plan: "Claude Max",
quotas: {
daily: { usedPercentage: 35, remainingPercentage: 65, resetAt: "2026-05-30T00:00:00.000Z" },
},
},
cache: { quotas: null, plan: null, message: null, fetchedAt: "" },
};
},
});
const status = await buildApiKeySelfServiceStatus(metadata, deps);
assert.deepEqual(fetches, ["conn-codex", "conn-claude"]);
assert.deepEqual(
status.accountQuotas.map((quota: { connectionId: string }) => quota.connectionId),
["conn-codex", "conn-claude"]
);
assert.equal(status.accountQuotas[0].provider, "codex");
assert.equal(status.accountQuotas[0].plan, "ChatGPT Plus");
assert.equal(status.accountQuotas[0].quotas.session.remainingPercentage, 99);
assert.equal(status.accountQuotas[1].provider, "claude");
assert.equal(status.accountQuotas[1].plan, "Claude Max");
assert.equal(status.accountQuotas[1].quotas.daily.usedPercentage, 35);
});
test("self-service status reports all active provider account quotas for unrestricted keys", async () => {
const metadata = {
id: "key-unrestricted",
name: "unrestricted",
scopes: [SELF_USAGE_SCOPE, SELF_ACCOUNT_QUOTA_SCOPE],
allowedConnections: [],
};
const { deps } = makeDeps({
getProviderConnections: async () => [
{ id: "conn-codex", provider: "codex", isActive: true },
{ id: "conn-cursor", provider: "cursor", isActive: true },
{ id: "conn-disabled", provider: "claude", isActive: false },
],
fetchAndPersistProviderLimits: async (connectionId: string) => ({
connection: { id: connectionId, provider: connectionId === "conn-codex" ? "codex" : "cursor" },
usage: {
plan: connectionId === "conn-codex" ? "ChatGPT Plus" : "Cursor Pro",
quotas: {
monthly: { used: 25, remaining: 75, resetAt: "2026-06-01T00:00:00.000Z" },
},
},
cache: { quotas: null, plan: null, message: null, fetchedAt: "" },
}),
});
const status = await buildApiKeySelfServiceStatus(metadata, deps);
assert.deepEqual(
status.accountQuotas.map((quota: { connectionId: string }) => quota.connectionId),
["conn-codex", "conn-cursor"]
);
assert.equal(status.accountQuotas[0].quotas.monthly.remainingPercentage, 75);
assert.equal(status.accountQuotas[1].plan, "Cursor Pro");
});
test("self-service status isolates provider account quota fetch failures per connection", async () => {
const metadata = {
id: "key-partial",
name: "partial",
scopes: [SELF_USAGE_SCOPE, SELF_ACCOUNT_QUOTA_SCOPE],
allowedConnections: ["conn-codex", "conn-cursor"],
};
const { deps } = makeDeps({
getProviderConnectionById: async (connectionId: string) => ({
id: connectionId,
provider: connectionId === "conn-codex" ? "codex" : "cursor",
isActive: true,
}),
fetchAndPersistProviderLimits: async (connectionId: string) => {
if (connectionId === "conn-cursor") throw new Error("upstream unavailable");
return {
connection: { id: connectionId, provider: "codex" },
usage: {
quotas: {
weekly: { used: 40, remaining: 60, resetAt: "2026-06-01T00:00:00.000Z" },
},
},
cache: { quotas: null, plan: null, message: null, fetchedAt: "" },
};
},
});
const status = await buildApiKeySelfServiceStatus(metadata, deps);
assert.equal(status.accountQuotas[0].connectionId, "conn-codex");
assert.equal(status.accountQuotas[0].quotas.weekly.remainingPercentage, 60);
assert.deepEqual(status.accountQuotas[1], {
provider: "cursor",
connectionId: "conn-cursor",
shared: true,
available: false,
reason: "fetch_failed",
});
});
test("self-service status isolates explicit provider connection lookup failures", async () => {
const metadata = {
id: "key-lookup-partial",
name: "lookup partial",
scopes: [SELF_USAGE_SCOPE, SELF_ACCOUNT_QUOTA_SCOPE],
allowedConnections: ["conn-codex", "conn-missing"],
};
const { deps } = makeDeps({
getProviderConnectionById: async (connectionId: string) => {
if (connectionId === "conn-missing") throw new Error("database unavailable");
return {
id: connectionId,
provider: "codex",
isActive: true,
};
},
fetchAndPersistProviderLimits: async (connectionId: string) => ({
connection: { id: connectionId, provider: "codex" },
usage: {
quotas: {
weekly: { used: 40, remaining: 60, resetAt: "2026-06-01T00:00:00.000Z" },
},
},
cache: { quotas: null, plan: null, message: null, fetchedAt: "" },
}),
});
const status = await buildApiKeySelfServiceStatus(metadata, deps);
assert.equal(status.accountQuotas[0].connectionId, "conn-codex");
assert.equal(status.accountQuotas[0].quotas.weekly.remainingPercentage, 60);
assert.deepEqual(status.accountQuotas[1], {
provider: "unknown",
connectionId: "conn-missing",
shared: true,
available: false,
reason: "connection_lookup_failed",
});
});
test("self-service status keeps usage visible when unrestricted provider lookup fails", async () => {
const metadata = {
id: "key-lookup-failed",
name: "lookup failed",
scopes: [SELF_USAGE_SCOPE, SELF_ACCOUNT_QUOTA_SCOPE],
allowedConnections: [],
};
const { deps } = makeDeps({
getProviderConnections: async () => {
throw new Error("database unavailable");
},
});
const status = await buildApiKeySelfServiceStatus(metadata, deps);
assert.equal(status.usage.cost.usedUsd, 12.34);
assert.deepEqual(status.accountQuotas, []);
assert.equal("accountQuota" in status, false);
});
test("self-service status normalizes Codex account quota for one explicit connection", async () => {
const metadata = {
id: "key-codex",
name: "codex",
scopes: [SELF_USAGE_SCOPE, SELF_ACCOUNT_QUOTA_SCOPE],
allowedConnections: ["conn-codex"],
};
const { deps } = makeDeps({
getProviderConnectionById: async (connectionId: string) => ({
id: connectionId,
provider: "codex",
}),
fetchAndPersistProviderLimits: async () => ({
connection: { id: "conn-codex", provider: "codex" },
usage: {
quotas: {
session: { used: 1, remaining: 99, resetAt: "2026-05-29T18:11:44.000Z" },
weekly: { used: 97, remaining: 3, resetAt: "2026-05-31T01:23:38.000Z" },
},
},
cache: { quotas: null, plan: null, message: null, fetchedAt: "" },
}),
});
const status = await buildApiKeySelfServiceStatus(metadata, deps);
assert.equal(status.accountQuotas.length, 1);
assert.deepEqual(status.accountQuotas[0], status.accountQuota);
assert.deepEqual(status.accountQuota, {
provider: "codex",
connectionId: "conn-codex",
shared: true,
quotas: {
session: {
usedPercentage: 1,
remainingPercentage: 99,
resetAt: "2026-05-29T18:11:44.000Z",
},
weekly: {
usedPercentage: 97,
remainingPercentage: 3,
resetAt: "2026-05-31T01:23:38.000Z",
},
},
});
});