chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
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";
|
||||
|
||||
import { makeManagementSessionRequest } from "../../helpers/managementSession.ts";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-combo-autopilot-"));
|
||||
const ORIGINAL_DATA_DIR = process.env.DATA_DIR;
|
||||
const ORIGINAL_INITIAL_PASSWORD = process.env.INITIAL_PASSWORD;
|
||||
const ORIGINAL_JWT_SECRET = process.env.JWT_SECRET;
|
||||
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const core = await import("../../../src/lib/db/core.ts");
|
||||
const combosDb = await import("../../../src/lib/db/combos.ts");
|
||||
const settingsDb = await import("../../../src/lib/db/settings.ts");
|
||||
const quotaSnapshotsDb = await import("../../../src/lib/db/quotaSnapshots.ts");
|
||||
const callLogs = await import("../../../src/lib/usage/callLogs.ts");
|
||||
const comboMetrics = await import("../../../open-sse/services/comboMetrics.ts");
|
||||
const comboAutopilot = await import("../../../src/lib/monitoring/comboHealthAutopilot.ts");
|
||||
const route = await import("../../../src/app/api/usage/combo-health-autopilot/route.ts");
|
||||
const { normalizeComboStep } = await import("../../../src/lib/combos/steps.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
comboMetrics.resetAllComboMetrics();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
async function enableManagementAuth() {
|
||||
process.env.INITIAL_PASSWORD = "combo-autopilot-password";
|
||||
await settingsDb.updateSettings({ requireLogin: true, password: "" });
|
||||
}
|
||||
|
||||
async function seedDegradedCombo() {
|
||||
const comboInput = {
|
||||
name: "combo-autopilot-degraded",
|
||||
strategy: "priority",
|
||||
models: [
|
||||
{
|
||||
kind: "model",
|
||||
providerId: "openai",
|
||||
model: "openai/gpt-4o-mini",
|
||||
connectionId: "combo-autopilot-conn-a",
|
||||
label: "Autopilot A",
|
||||
},
|
||||
{
|
||||
kind: "model",
|
||||
providerId: "anthropic",
|
||||
model: "anthropic/claude-3-5-haiku",
|
||||
connectionId: "combo-autopilot-conn-b",
|
||||
label: "Autopilot B",
|
||||
},
|
||||
],
|
||||
};
|
||||
const combo = await combosDb.createCombo(comboInput);
|
||||
const firstStep = normalizeComboStep(comboInput.models[0], {
|
||||
comboName: comboInput.name,
|
||||
index: 0,
|
||||
});
|
||||
|
||||
for (let index = 0; index < 5; index += 1) {
|
||||
await callLogs.saveCallLog({
|
||||
id: `combo-autopilot-log-${index}`,
|
||||
timestamp: new Date(Date.now() - index * 60_000).toISOString(),
|
||||
method: "POST",
|
||||
path: "/v1/chat/completions",
|
||||
status: 503,
|
||||
model: "openai/gpt-4o-mini",
|
||||
requestedModel: comboInput.name,
|
||||
provider: "openai",
|
||||
connectionId: "combo-autopilot-conn-a",
|
||||
duration: 500,
|
||||
comboName: comboInput.name,
|
||||
comboStepId: firstStep.id,
|
||||
comboExecutionKey: firstStep.id,
|
||||
error: "upstream unavailable",
|
||||
});
|
||||
}
|
||||
|
||||
quotaSnapshotsDb.saveQuotaSnapshot({
|
||||
provider: "openai",
|
||||
connection_id: "combo-autopilot-conn-a",
|
||||
window_key: "daily",
|
||||
remaining_percentage: 3,
|
||||
is_exhausted: 0,
|
||||
next_reset_at: null,
|
||||
window_duration_ms: 86_400_000,
|
||||
raw_data: null,
|
||||
});
|
||||
|
||||
return { combo, comboInput, firstStep };
|
||||
}
|
||||
|
||||
test.beforeEach(async () => {
|
||||
await resetStorage();
|
||||
});
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
|
||||
if (ORIGINAL_DATA_DIR === undefined) delete process.env.DATA_DIR;
|
||||
else process.env.DATA_DIR = ORIGINAL_DATA_DIR;
|
||||
|
||||
if (ORIGINAL_INITIAL_PASSWORD === undefined) delete process.env.INITIAL_PASSWORD;
|
||||
else process.env.INITIAL_PASSWORD = ORIGINAL_INITIAL_PASSWORD;
|
||||
|
||||
if (ORIGINAL_JWT_SECRET === undefined) delete process.env.JWT_SECRET;
|
||||
else process.env.JWT_SECRET = ORIGINAL_JWT_SECRET;
|
||||
});
|
||||
|
||||
test("combo health autopilot reports degraded combo issues and manual actions", async () => {
|
||||
const { combo } = await seedDegradedCombo();
|
||||
|
||||
const report = await comboAutopilot.buildComboHealthAutopilotReport({
|
||||
range: "24h",
|
||||
horizon: "7d",
|
||||
comboId: String(combo.id),
|
||||
includeHealthy: true,
|
||||
});
|
||||
|
||||
assert.equal(report.summary.comboCount, 1);
|
||||
assert.equal(report.status, "critical");
|
||||
assert.equal(report.combos.length, 1);
|
||||
assert.equal(report.combos[0].state, "down");
|
||||
assert.ok(report.combos[0].score < 100);
|
||||
|
||||
const issueKinds = new Set(report.combos[0].issues.map((issue) => issue.kind));
|
||||
assert.equal(issueKinds.has("combo_low_success_rate"), true);
|
||||
assert.equal(issueKinds.has("target_low_success_rate"), true);
|
||||
assert.equal(issueKinds.has("target_last_error"), true);
|
||||
assert.equal(issueKinds.has("target_low_quota"), true);
|
||||
assert.ok(report.combos[0].issues.some((issue) => issue.actions.length > 0));
|
||||
});
|
||||
|
||||
test("combo health autopilot supports report-only mode without actions", async () => {
|
||||
const { combo } = await seedDegradedCombo();
|
||||
|
||||
const report = await comboAutopilot.buildComboHealthAutopilotReport({
|
||||
range: "24h",
|
||||
horizon: "7d",
|
||||
comboId: String(combo.id),
|
||||
includeHealthy: true,
|
||||
includeActions: false,
|
||||
});
|
||||
|
||||
assert.equal(report.combos.length, 1);
|
||||
assert.ok(report.combos[0].issues.length > 0);
|
||||
assert.equal(
|
||||
report.combos[0].issues.every((issue) => issue.actions.length === 0),
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test("combo health autopilot route requires auth, validates query, and returns 404", async () => {
|
||||
await enableManagementAuth();
|
||||
const { combo } = await seedDegradedCombo();
|
||||
|
||||
const unauthenticated = await route.GET(
|
||||
new Request(`http://localhost/api/usage/combo-health-autopilot?comboId=${combo.id}`)
|
||||
);
|
||||
assert.equal(unauthenticated.status, 401);
|
||||
|
||||
const invalid = await route.GET(
|
||||
await makeManagementSessionRequest(
|
||||
"http://localhost/api/usage/combo-health-autopilot?range=bad"
|
||||
)
|
||||
);
|
||||
assert.equal(invalid.status, 400);
|
||||
|
||||
const missing = await route.GET(
|
||||
await makeManagementSessionRequest(
|
||||
"http://localhost/api/usage/combo-health-autopilot?comboId=11111111-1111-4111-8111-111111111111"
|
||||
)
|
||||
);
|
||||
assert.equal(missing.status, 404);
|
||||
|
||||
const authenticated = await route.GET(
|
||||
await makeManagementSessionRequest(
|
||||
`http://localhost/api/usage/combo-health-autopilot?range=24h&horizon=7d&includeActions=false&comboId=${combo.id}`
|
||||
)
|
||||
);
|
||||
assert.equal(authenticated.status, 200);
|
||||
const body = await authenticated.json();
|
||||
assert.equal(body.summary.comboCount, 1);
|
||||
assert.equal(
|
||||
body.combos[0].issues.every((issue: { actions: unknown[] }) => issue.actions.length === 0),
|
||||
true
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { glmMonthlyRemainingPercentage } from "../../../open-sse/services/usage.ts";
|
||||
|
||||
// #3580 — z.ai/GLM coding plans have no monthly cap (only 5-hour windows), so the
|
||||
// quota API reports the TIME_LIMIT ("Monthly") entry with total=0. The previous
|
||||
// `total > 0 ? … : 0` fallback rendered that as a misleading "Monthly 0% remaining",
|
||||
// which can skew downstream model-choice. With no absolute cap we now fall back to the
|
||||
// percentage-derived remaining (full/100% when 0% used).
|
||||
|
||||
test("#3580 no monthly cap (total=0), 0% used → 100% remaining (was 0%)", () => {
|
||||
assert.equal(glmMonthlyRemainingPercentage(0, 100), 100);
|
||||
});
|
||||
|
||||
test("#3580 no monthly cap (total=0), no usage signal → defaults to full (100)", () => {
|
||||
// remaining defaults to max(0, 100 - percentage); 0% used → 100
|
||||
assert.equal(glmMonthlyRemainingPercentage(0, 100), 100);
|
||||
});
|
||||
|
||||
test("#3580 absolute monthly cap still computes remaining/total", () => {
|
||||
assert.equal(glmMonthlyRemainingPercentage(1000, 500), 50);
|
||||
assert.equal(glmMonthlyRemainingPercentage(1000, 1000), 100);
|
||||
assert.equal(glmMonthlyRemainingPercentage(1000, 0), 0);
|
||||
});
|
||||
|
||||
test("#3580 clamps out-of-range values to [0,100]", () => {
|
||||
assert.equal(glmMonthlyRemainingPercentage(0, 250), 100); // no cap, absolute remaining > 100 → clamp full
|
||||
assert.equal(glmMonthlyRemainingPercentage(1000, 5000), 100);
|
||||
assert.equal(glmMonthlyRemainingPercentage(0, -5), 0);
|
||||
});
|
||||
@@ -0,0 +1,256 @@
|
||||
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";
|
||||
import { NextRequest } from "next/server";
|
||||
import { makeManagementSessionRequest } from "../../helpers/managementSession.ts";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-health-autopilot-"));
|
||||
const ORIGINAL_DATA_DIR = process.env.DATA_DIR;
|
||||
const ORIGINAL_INITIAL_PASSWORD = process.env.INITIAL_PASSWORD;
|
||||
const ORIGINAL_JWT_SECRET = process.env.JWT_SECRET;
|
||||
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const core = await import("../../../src/lib/db/core.ts");
|
||||
const settingsDb = await import("../../../src/lib/db/settings.ts");
|
||||
const providersDb = await import("../../../src/lib/db/providers.ts");
|
||||
const autopilot = await import("../../../src/lib/monitoring/providerHealthAutopilot.ts");
|
||||
const actionsRoute = await import("../../../src/app/api/providers/health-autopilot/actions/route.ts");
|
||||
const reportRoute = await import("../../../src/app/api/providers/health-autopilot/route.ts");
|
||||
const routeGuard = await import("../../../src/server/authz/routeGuard.ts");
|
||||
const authzPipeline = await import("../../../src/server/authz/pipeline.ts");
|
||||
const accountFallback = await import("@omniroute/open-sse/services/accountFallback");
|
||||
|
||||
const PROVIDER = "autopilot-test-provider";
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
async function enableManagementAuth() {
|
||||
process.env.INITIAL_PASSWORD = "autopilot-password";
|
||||
await settingsDb.updateSettings({ requireLogin: true, password: "" });
|
||||
}
|
||||
|
||||
async function createCooldownConnection(provider = PROVIDER) {
|
||||
return providersDb.createProviderConnection({
|
||||
provider,
|
||||
authType: "apikey",
|
||||
name: "cooling-key",
|
||||
apiKey: "test-key",
|
||||
isActive: true,
|
||||
testStatus: "unavailable",
|
||||
lastError: "rate limited",
|
||||
lastErrorType: "upstream_rate_limited",
|
||||
errorCode: "429",
|
||||
rateLimitedUntil: new Date(Date.now() + 60_000).toISOString(),
|
||||
}) as Promise<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
function findAction(report: autopilot.ProviderAutopilotReport, type: string) {
|
||||
for (const provider of report.providers) {
|
||||
for (const issue of provider.issues) {
|
||||
const action = issue.actions.find((candidate) => candidate.type === type);
|
||||
if (action) return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
test.beforeEach(async () => {
|
||||
accountFallback.clearProviderFailure(PROVIDER);
|
||||
await resetStorage();
|
||||
});
|
||||
|
||||
test.after(async () => {
|
||||
accountFallback.clearProviderFailure(PROVIDER);
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
|
||||
if (ORIGINAL_DATA_DIR === undefined) delete process.env.DATA_DIR;
|
||||
else process.env.DATA_DIR = ORIGINAL_DATA_DIR;
|
||||
|
||||
if (ORIGINAL_INITIAL_PASSWORD === undefined) delete process.env.INITIAL_PASSWORD;
|
||||
else process.env.INITIAL_PASSWORD = ORIGINAL_INITIAL_PASSWORD;
|
||||
|
||||
if (ORIGINAL_JWT_SECRET === undefined) delete process.env.JWT_SECRET;
|
||||
else process.env.JWT_SECRET = ORIGINAL_JWT_SECRET;
|
||||
});
|
||||
|
||||
test("provider health autopilot reports actionable cooldown and model lockout issues", async () => {
|
||||
const connection = await createCooldownConnection();
|
||||
accountFallback.lockModel(
|
||||
PROVIDER,
|
||||
String(connection.id),
|
||||
"locked-model",
|
||||
"quota_exhausted",
|
||||
60_000,
|
||||
{}
|
||||
);
|
||||
|
||||
try {
|
||||
const report = await autopilot.buildProviderHealthAutopilotReport({
|
||||
provider: PROVIDER,
|
||||
includeHealthy: true,
|
||||
});
|
||||
|
||||
assert.equal(report.status, "warning");
|
||||
assert.equal(report.summary.connectionCount, 1);
|
||||
assert.ok(report.summary.issueCount >= 2);
|
||||
assert.ok(findAction(report, "clear_connection_cooldown"));
|
||||
assert.ok(findAction(report, "clear_model_lockout"));
|
||||
|
||||
const provider = report.providers.find((entry) => entry.provider === PROVIDER);
|
||||
assert.ok(provider);
|
||||
assert.equal(provider.signals.connections.cooldown, 1);
|
||||
assert.equal(provider.signals.modelLockouts, 1);
|
||||
} finally {
|
||||
accountFallback.clearModelLock(PROVIDER, String(connection.id), "locked-model");
|
||||
}
|
||||
});
|
||||
|
||||
test("provider health autopilot action clears cooldown with stale-state protection", async () => {
|
||||
await enableManagementAuth();
|
||||
const connection = await createCooldownConnection();
|
||||
const report = await autopilot.buildProviderHealthAutopilotReport({
|
||||
provider: PROVIDER,
|
||||
includeHealthy: true,
|
||||
});
|
||||
const action = findAction(report, "clear_connection_cooldown");
|
||||
assert.ok(action);
|
||||
|
||||
const unauthenticated = await actionsRoute.POST(
|
||||
new Request("http://localhost/api/providers/health-autopilot/actions", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
type: action.type,
|
||||
target: action.target,
|
||||
preconditionsHash: action.preconditionsHash,
|
||||
confirm: true,
|
||||
}),
|
||||
})
|
||||
);
|
||||
assert.equal(unauthenticated.status, 401);
|
||||
|
||||
const stale = await actionsRoute.POST(
|
||||
await makeManagementSessionRequest("http://localhost/api/providers/health-autopilot/actions", {
|
||||
method: "POST",
|
||||
body: {
|
||||
type: action.type,
|
||||
target: action.target,
|
||||
preconditionsHash: "stale-hash",
|
||||
confirm: true,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.equal(stale.status, 409);
|
||||
|
||||
const applied = await actionsRoute.POST(
|
||||
await makeManagementSessionRequest("http://localhost/api/providers/health-autopilot/actions", {
|
||||
method: "POST",
|
||||
headers: { origin: "http://localhost", "sec-fetch-site": "same-origin" },
|
||||
body: {
|
||||
type: action.type,
|
||||
target: action.target,
|
||||
preconditionsHash: action.preconditionsHash,
|
||||
confirm: true,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.equal(applied.status, 200);
|
||||
const body = await applied.json();
|
||||
assert.equal(body.success, true);
|
||||
|
||||
const updated = (await providersDb.getProviderConnectionById(String(connection.id))) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
assert.equal(updated.rateLimitedUntil, undefined);
|
||||
assert.equal(updated.lastError, undefined);
|
||||
assert.equal(updated.testStatus, "active");
|
||||
});
|
||||
|
||||
test("provider health autopilot action rejects cross-site mutations", async () => {
|
||||
// Cross-site origin validation for browser mutations is centralized in the authz
|
||||
// pipeline (#5278): the per-route same-origin check was removed from the actions
|
||||
// handler and is now enforced by validateBrowserMutationOrigin inside runAuthzPipeline
|
||||
// for MANAGEMENT routes with an unsafe method + dashboard session. Drive the request
|
||||
// through the pipeline (the real enforcement point) and assert it is blocked with 403
|
||||
// before the route runs, leaving the connection untouched.
|
||||
await enableManagementAuth();
|
||||
const connection = await createCooldownConnection();
|
||||
const report = await autopilot.buildProviderHealthAutopilotReport({
|
||||
provider: PROVIDER,
|
||||
includeHealthy: true,
|
||||
});
|
||||
const action = findAction(report, "clear_connection_cooldown");
|
||||
assert.ok(action);
|
||||
|
||||
const rawRequest = await makeManagementSessionRequest(
|
||||
"http://localhost/api/providers/health-autopilot/actions",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { origin: "https://evil.example", "sec-fetch-site": "cross-site" },
|
||||
body: {
|
||||
type: action.type,
|
||||
target: action.target,
|
||||
preconditionsHash: action.preconditionsHash,
|
||||
confirm: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
const response = await authzPipeline.runAuthzPipeline(new NextRequest(rawRequest), {
|
||||
enforce: true,
|
||||
});
|
||||
|
||||
assert.equal(response.status, 403);
|
||||
assert.equal(response.headers.get("x-omniroute-route-class"), "MANAGEMENT");
|
||||
// The pipeline blocks before the route handler runs, so the cooldown is untouched.
|
||||
const unchanged = (await providersDb.getProviderConnectionById(String(connection.id))) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
assert.ok(unchanged.rateLimitedUntil);
|
||||
});
|
||||
|
||||
test("provider health autopilot action rejects malformed JSON", async () => {
|
||||
await enableManagementAuth();
|
||||
|
||||
const response = await actionsRoute.POST(
|
||||
await makeManagementSessionRequest("http://localhost/api/providers/health-autopilot/actions", {
|
||||
method: "POST",
|
||||
body: "{not-json",
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(response.status, 400);
|
||||
const body = await response.json();
|
||||
assert.equal(body.error.message, "Invalid JSON body");
|
||||
});
|
||||
|
||||
test("provider health autopilot action route is always protected", () => {
|
||||
assert.equal(routeGuard.isAlwaysProtectedPath("/api/providers/health-autopilot/actions"), true);
|
||||
});
|
||||
|
||||
test("provider health autopilot report route requires management auth", async () => {
|
||||
await enableManagementAuth();
|
||||
await createCooldownConnection();
|
||||
|
||||
const unauthenticated = await reportRoute.GET(
|
||||
new Request("http://localhost/api/providers/health-autopilot")
|
||||
);
|
||||
assert.equal(unauthenticated.status, 401);
|
||||
|
||||
const authenticated = await reportRoute.GET(
|
||||
await makeManagementSessionRequest(
|
||||
"http://localhost/api/providers/health-autopilot?includeHealthy=true"
|
||||
)
|
||||
);
|
||||
assert.equal(authenticated.status, 200);
|
||||
const body = await authenticated.json();
|
||||
assert.equal(body.summary.connectionCount, 1);
|
||||
});
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* tests/unit/quota-division-blocks.test.ts
|
||||
*
|
||||
* Task A2 — per-key quota division blocks for countable units.
|
||||
*
|
||||
* Regression: before the fix, enforce.ts derived consumedTotal from the
|
||||
* saturation signal (globalUsedPercent × effectiveLimit), which is always 0
|
||||
* for countable units (requests/tokens/usd). As a result, the pool was never
|
||||
* considered "saturated" and enforceQuotaShare never returned "block" for
|
||||
* countable-unit overage, regardless of actual consumption.
|
||||
*
|
||||
* After the fix, enforce.ts uses store.poolConsumedTotal() for countable units,
|
||||
* which sums real per-key consumption from the store. When the pool total ≥
|
||||
* effectiveLimit the decision is "block" (global-saturated path in decideFairShare).
|
||||
*
|
||||
* Test scenarios (integration through enforceQuotaShare + real SQLite store):
|
||||
* A. Pool total OVER budget → block (global-saturated fires when
|
||||
* consumedTotal ≥ effectiveLimit).
|
||||
* B. Pool total UNDER budget, key UNDER fair-share → allow.
|
||||
*
|
||||
* Both scenarios run within a SINGLE test against the SAME SQLite DB using
|
||||
* distinct connection IDs / pool IDs so their consumptions are fully isolated.
|
||||
*
|
||||
* Setup:
|
||||
* - Custom provider "test-provider-a2" (not in catalog → manual plan only).
|
||||
* - provider_plans: requests/hourly/limit=100 for each connection.
|
||||
* - accountCount=1 → effectiveLimit=100; fairShare for 50%-weight key = 50.
|
||||
* - Pool with 1 connection, 2 API keys at 50/50 hard policy per scenario.
|
||||
*
|
||||
* Scenario A: keyA=60, keyB=60 → poolTotal=120 ≥ effectiveLimit=100 → block.
|
||||
* Scenario B: keyA=20, keyB=20 → poolTotal=40 < effectiveLimit=100 → allow.
|
||||
*/
|
||||
|
||||
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";
|
||||
|
||||
// ── Set DATA_DIR BEFORE any DB import (resolved as module-level constant) ───
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-division-blocks-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
// ── Imports ──────────────────────────────────────────────────────────────────
|
||||
const providerPlans = await import("../../../src/lib/db/providerPlans.ts");
|
||||
const quotaPools = await import("../../../src/lib/db/quotaPools.ts");
|
||||
const { SqliteQuotaStore } = await import("../../../src/lib/quota/sqliteQuotaStore.ts");
|
||||
const { enforceQuotaShare } = await import("../../../src/lib/quota/enforce.ts");
|
||||
const core = await import("../../../src/lib/db/core.ts");
|
||||
|
||||
// ── Suite cleanup ─────────────────────────────────────────────────────────────
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
if (fs.existsSync(TEST_DATA_DIR)) {
|
||||
try { fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); } catch { /* ignore */ }
|
||||
}
|
||||
});
|
||||
|
||||
// ── Constants ─────────────────────────────────────────────────────────────────
|
||||
const LIMIT = 100; // per-account plan limit; accountCount=1 → effectiveLimit=100
|
||||
const store = new SqliteQuotaStore();
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Single suite: both scenarios share a DB, isolated by distinct pool/conn IDs
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
await test("quota-division-blocks: countable-unit enforcement (block + allow)", async (t) => {
|
||||
// ── Scenario A: pool total > effectiveLimit → block ──────────────────────
|
||||
await t.test(
|
||||
"[A] pool total > effectiveLimit → block (global-saturated)",
|
||||
async () => {
|
||||
const CONN = "conn-block-a";
|
||||
const PROV = "test-provider-a2-block";
|
||||
const KEY_A = "key-block-a1";
|
||||
const KEY_B = "key-block-b1";
|
||||
|
||||
// Seed plan: requests/hourly/limit=100
|
||||
providerPlans.upsertPlan(
|
||||
CONN,
|
||||
PROV,
|
||||
[{ unit: "requests", window: "hourly", limit: LIMIT }],
|
||||
"manual"
|
||||
);
|
||||
|
||||
// Create pool: 2 allocations at 50/50 hard
|
||||
const pool = quotaPools.createPool({
|
||||
connectionId: CONN,
|
||||
name: "Block Pool A",
|
||||
allocations: [
|
||||
{ apiKeyId: KEY_A, weight: 50, policy: "hard" },
|
||||
{ apiKeyId: KEY_B, weight: 50, policy: "hard" },
|
||||
],
|
||||
});
|
||||
|
||||
const dim = { poolId: pool.id, unit: "requests" as const, window: "hourly" as const };
|
||||
|
||||
// Consume: keyA=60, keyB=60 → poolTotal=120 > effectiveLimit=100
|
||||
await store.consume(KEY_A, dim, 60);
|
||||
await store.consume(KEY_B, dim, 60);
|
||||
|
||||
const decision = await enforceQuotaShare({
|
||||
apiKeyId: KEY_A,
|
||||
connectionId: CONN,
|
||||
provider: PROV,
|
||||
estimatedCost: { requests: 1 },
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
decision.kind,
|
||||
"block",
|
||||
`[A] Expected block when poolTotal(120) ≥ effectiveLimit(100); got: ${JSON.stringify(decision)}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// ── Scenario B: pool total < effectiveLimit, key under fair-share → allow ─
|
||||
await t.test(
|
||||
"[B] pool total < effectiveLimit and key under fair-share → allow",
|
||||
async () => {
|
||||
const CONN = "conn-allow-b";
|
||||
const PROV = "test-provider-a2-allow";
|
||||
const KEY_A = "key-allow-a1";
|
||||
const KEY_B = "key-allow-b1";
|
||||
|
||||
// Seed plan for separate connection: requests/hourly/limit=100
|
||||
providerPlans.upsertPlan(
|
||||
CONN,
|
||||
PROV,
|
||||
[{ unit: "requests", window: "hourly", limit: LIMIT }],
|
||||
"manual"
|
||||
);
|
||||
|
||||
// Create pool: distinct from Scenario A (different poolId + connection)
|
||||
const pool = quotaPools.createPool({
|
||||
connectionId: CONN,
|
||||
name: "Allow Pool B",
|
||||
allocations: [
|
||||
{ apiKeyId: KEY_A, weight: 50, policy: "hard" },
|
||||
{ apiKeyId: KEY_B, weight: 50, policy: "hard" },
|
||||
],
|
||||
});
|
||||
|
||||
const dim = { poolId: pool.id, unit: "requests" as const, window: "hourly" as const };
|
||||
|
||||
// Consume: keyA=20, keyB=20 → poolTotal=40 < effectiveLimit=100
|
||||
await store.consume(KEY_A, dim, 20);
|
||||
await store.consume(KEY_B, dim, 20);
|
||||
|
||||
const decision = await enforceQuotaShare({
|
||||
apiKeyId: KEY_A,
|
||||
connectionId: CONN,
|
||||
provider: PROV,
|
||||
estimatedCost: { requests: 1 },
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
decision.kind,
|
||||
"allow",
|
||||
`[B] Expected allow when poolTotal(40) < effectiveLimit(100); got: ${JSON.stringify(decision)}`
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user