mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 23:02:10 +03:00
test(quota): cover dimensions, schemas, plan registry
This commit is contained in:
203
tests/unit/quota-dimensions.test.ts
Normal file
203
tests/unit/quota-dimensions.test.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
QuotaUnitSchema,
|
||||
QuotaWindowSchema,
|
||||
PolicySchema,
|
||||
QuotaDimensionSchema,
|
||||
PoolAllocationSchema,
|
||||
ProviderPlanSchema,
|
||||
QuotaPoolSchema,
|
||||
WINDOW_MS,
|
||||
dimensionKeyToString,
|
||||
} from "../../src/lib/quota/dimensions";
|
||||
|
||||
test("QuotaUnitSchema accepts all 4 valid units", () => {
|
||||
for (const u of ["percent", "requests", "tokens", "usd"] as const) {
|
||||
const r = QuotaUnitSchema.safeParse(u);
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data, u);
|
||||
}
|
||||
});
|
||||
|
||||
test("QuotaUnitSchema rejects unknown unit", () => {
|
||||
assert.equal(QuotaUnitSchema.safeParse("bytes").success, false);
|
||||
});
|
||||
|
||||
test("QuotaWindowSchema accepts all 5 valid windows", () => {
|
||||
for (const w of ["5h", "hourly", "daily", "weekly", "monthly"] as const) {
|
||||
const r = QuotaWindowSchema.safeParse(w);
|
||||
assert.ok(r.success);
|
||||
}
|
||||
});
|
||||
|
||||
test("QuotaWindowSchema rejects unknown window", () => {
|
||||
assert.equal(QuotaWindowSchema.safeParse("yearly").success, false);
|
||||
});
|
||||
|
||||
test("PolicySchema accepts hard/soft/burst", () => {
|
||||
for (const p of ["hard", "soft", "burst"] as const) {
|
||||
assert.ok(PolicySchema.safeParse(p).success);
|
||||
}
|
||||
});
|
||||
|
||||
test("PolicySchema rejects unknown policy", () => {
|
||||
assert.equal(PolicySchema.safeParse("strict").success, false);
|
||||
});
|
||||
|
||||
test("QuotaDimensionSchema parses valid dimension", () => {
|
||||
const r = QuotaDimensionSchema.safeParse({ unit: "percent", window: "5h", limit: 100 });
|
||||
assert.ok(r.success);
|
||||
assert.deepEqual(r.data, { unit: "percent", window: "5h", limit: 100 });
|
||||
});
|
||||
|
||||
test("QuotaDimensionSchema rejects limit <= 0", () => {
|
||||
assert.equal(
|
||||
QuotaDimensionSchema.safeParse({ unit: "tokens", window: "daily", limit: 0 }).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test("QuotaDimensionSchema rejects negative limit", () => {
|
||||
assert.equal(
|
||||
QuotaDimensionSchema.safeParse({ unit: "tokens", window: "daily", limit: -1 }).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test("PoolAllocationSchema parses valid allocation", () => {
|
||||
const r = PoolAllocationSchema.safeParse({ apiKeyId: "k-abc", weight: 50, policy: "hard" });
|
||||
assert.ok(r.success);
|
||||
});
|
||||
|
||||
test("PoolAllocationSchema rejects weight > 100", () => {
|
||||
assert.equal(
|
||||
PoolAllocationSchema.safeParse({ apiKeyId: "k", weight: 101, policy: "soft" }).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test("PoolAllocationSchema rejects empty apiKeyId", () => {
|
||||
assert.equal(
|
||||
PoolAllocationSchema.safeParse({ apiKeyId: "", weight: 50, policy: "hard" }).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test("PoolAllocationSchema accepts capValue + capUnit", () => {
|
||||
const r = PoolAllocationSchema.safeParse({
|
||||
apiKeyId: "k1",
|
||||
weight: 30,
|
||||
policy: "burst",
|
||||
capValue: 1000,
|
||||
capUnit: "tokens",
|
||||
});
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data?.capValue, 1000);
|
||||
});
|
||||
|
||||
test("ProviderPlanSchema parses valid plan", () => {
|
||||
const r = ProviderPlanSchema.safeParse({
|
||||
connectionId: "conn-1",
|
||||
provider: "codex",
|
||||
dimensions: [{ unit: "percent", window: "5h", limit: 100 }],
|
||||
source: "auto",
|
||||
});
|
||||
assert.ok(r.success);
|
||||
});
|
||||
|
||||
test("ProviderPlanSchema accepts connectionId=null", () => {
|
||||
const r = ProviderPlanSchema.safeParse({
|
||||
connectionId: null,
|
||||
provider: "openai",
|
||||
dimensions: [{ unit: "tokens", window: "hourly", limit: 1000 }],
|
||||
source: "manual",
|
||||
});
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data?.connectionId, null);
|
||||
});
|
||||
|
||||
test("ProviderPlanSchema rejects empty dimensions array", () => {
|
||||
assert.equal(
|
||||
ProviderPlanSchema.safeParse({
|
||||
connectionId: "c",
|
||||
provider: "openai",
|
||||
dimensions: [],
|
||||
source: "manual",
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test("QuotaPoolSchema parses valid pool", () => {
|
||||
const r = QuotaPoolSchema.safeParse({
|
||||
id: "pool-1",
|
||||
connectionId: "conn-1",
|
||||
name: "My Pool",
|
||||
createdAt: "2024-01-01T00:00:00.000Z",
|
||||
allocations: [],
|
||||
});
|
||||
assert.ok(r.success);
|
||||
});
|
||||
|
||||
test("QuotaPoolSchema defaults allocations to empty array", () => {
|
||||
const r = QuotaPoolSchema.safeParse({
|
||||
id: "pool-2",
|
||||
connectionId: "conn-2",
|
||||
name: "Pool2",
|
||||
createdAt: "2024-06-01T00:00:00.000Z",
|
||||
});
|
||||
assert.ok(r.success);
|
||||
assert.deepEqual(r.data?.allocations, []);
|
||||
});
|
||||
|
||||
test("WINDOW_MS has correct value for hourly", () => {
|
||||
assert.equal(WINDOW_MS.hourly, 3_600_000);
|
||||
});
|
||||
|
||||
test("WINDOW_MS has correct value for 5h", () => {
|
||||
assert.equal(WINDOW_MS["5h"], 18_000_000);
|
||||
});
|
||||
|
||||
test("WINDOW_MS has correct value for daily", () => {
|
||||
assert.equal(WINDOW_MS.daily, 86_400_000);
|
||||
});
|
||||
|
||||
test("WINDOW_MS has correct value for weekly", () => {
|
||||
assert.equal(WINDOW_MS.weekly, 604_800_000);
|
||||
});
|
||||
|
||||
test("WINDOW_MS has correct value for monthly (30 days approximation)", () => {
|
||||
assert.equal(WINDOW_MS.monthly, 30 * 86_400_000);
|
||||
});
|
||||
|
||||
test("WINDOW_MS covers all 5 windows", () => {
|
||||
for (const w of ["5h", "hourly", "daily", "weekly", "monthly"] as const) {
|
||||
assert.ok(WINDOW_MS[w] > 0);
|
||||
}
|
||||
});
|
||||
|
||||
test("dimensionKeyToString produces stable colon-separated string", () => {
|
||||
assert.equal(
|
||||
dimensionKeyToString({ poolId: "pool-abc", unit: "percent", window: "5h" }),
|
||||
"pool-abc:percent:5h"
|
||||
);
|
||||
});
|
||||
|
||||
test("dimensionKeyToString parts are recoverable", () => {
|
||||
const s = dimensionKeyToString({ poolId: "my-pool", unit: "tokens", window: "weekly" });
|
||||
assert.deepEqual(s.split(":"), ["my-pool", "tokens", "weekly"]);
|
||||
});
|
||||
|
||||
test("dimensionKeyToString has no collision across unit/window combos", () => {
|
||||
const seen = new Set<string>();
|
||||
for (const unit of ["percent", "requests", "tokens", "usd"] as const) {
|
||||
for (const window of ["5h", "hourly", "daily", "weekly", "monthly"] as const) {
|
||||
const s = dimensionKeyToString({ poolId: "p", unit, window });
|
||||
assert.ok(!seen.has(s), `collision for ${s}`);
|
||||
seen.add(s);
|
||||
}
|
||||
}
|
||||
assert.equal(seen.size, 20);
|
||||
});
|
||||
82
tests/unit/quota-plan-registry.test.ts
Normal file
82
tests/unit/quota-plan-registry.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { getKnownPlan, knownProviders } from "../../src/lib/quota/planRegistry";
|
||||
|
||||
test("getKnownPlan('codex') returns non-null with 2 dimensions", () => {
|
||||
const p = getKnownPlan("codex");
|
||||
assert.notEqual(p, null);
|
||||
assert.equal(p?.provider, "codex");
|
||||
assert.equal(p?.dimensions.length, 2);
|
||||
});
|
||||
|
||||
test("getKnownPlan('codex') first dimension is percent/5h/100", () => {
|
||||
const p = getKnownPlan("codex");
|
||||
assert.deepEqual(p?.dimensions[0], { unit: "percent", window: "5h", limit: 100 });
|
||||
});
|
||||
|
||||
test("getKnownPlan('codex') second dimension is percent/weekly/100", () => {
|
||||
const p = getKnownPlan("codex");
|
||||
assert.deepEqual(p?.dimensions[1], { unit: "percent", window: "weekly", limit: 100 });
|
||||
});
|
||||
|
||||
test("getKnownPlan('glm') has 2 dimensions, tokens unit", () => {
|
||||
const p = getKnownPlan("glm");
|
||||
assert.equal(p?.dimensions.length, 2);
|
||||
for (const d of p?.dimensions ?? []) {
|
||||
assert.equal(d.unit, "tokens");
|
||||
}
|
||||
});
|
||||
|
||||
test("getKnownPlan('minimax') has 2 dimensions", () => {
|
||||
const p = getKnownPlan("minimax");
|
||||
assert.equal(p?.dimensions.length, 2);
|
||||
});
|
||||
|
||||
test("getKnownPlan('bailian') has 3 dimensions (5h/weekly/monthly)", () => {
|
||||
const p = getKnownPlan("bailian");
|
||||
assert.equal(p?.dimensions.length, 3);
|
||||
const ws = p?.dimensions.map((d) => d.window);
|
||||
assert.ok(ws?.includes("5h"));
|
||||
assert.ok(ws?.includes("weekly"));
|
||||
assert.ok(ws?.includes("monthly"));
|
||||
});
|
||||
|
||||
test("getKnownPlan('kimi') has 1 dimension: requests/hourly/1500", () => {
|
||||
const p = getKnownPlan("kimi");
|
||||
assert.deepEqual(p?.dimensions, [{ unit: "requests", window: "hourly", limit: 1500 }]);
|
||||
});
|
||||
|
||||
test("getKnownPlan('alibaba') has 1 dimension: requests/monthly/90000", () => {
|
||||
const p = getKnownPlan("alibaba");
|
||||
assert.deepEqual(p?.dimensions, [{ unit: "requests", window: "monthly", limit: 90_000 }]);
|
||||
});
|
||||
|
||||
test("getKnownPlan('unknown') returns null", () => {
|
||||
assert.equal(getKnownPlan("unknown"), null);
|
||||
});
|
||||
|
||||
test("getKnownPlan('openai') returns null (manual obrigatório)", () => {
|
||||
assert.equal(getKnownPlan("openai"), null);
|
||||
});
|
||||
|
||||
test("getKnownPlan('') returns null", () => {
|
||||
assert.equal(getKnownPlan(""), null);
|
||||
});
|
||||
|
||||
test("knownProviders() returns exactly 6 entries", () => {
|
||||
assert.equal(knownProviders().length, 6);
|
||||
});
|
||||
|
||||
test("knownProviders() includes codex/glm/minimax/bailian/kimi/alibaba", () => {
|
||||
const list = knownProviders() as readonly string[];
|
||||
for (const p of ["codex", "glm", "minimax", "bailian", "kimi", "alibaba"]) {
|
||||
assert.ok(list.includes(p), `missing ${p}`);
|
||||
}
|
||||
});
|
||||
|
||||
test("every provider in knownProviders has a non-null plan", () => {
|
||||
for (const provider of knownProviders()) {
|
||||
assert.notEqual(getKnownPlan(provider), null, `getKnownPlan('${provider}') null`);
|
||||
}
|
||||
});
|
||||
167
tests/unit/quota-schemas.test.ts
Normal file
167
tests/unit/quota-schemas.test.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
PoolCreateSchema,
|
||||
PoolUpdateSchema,
|
||||
PlanUpsertSchema,
|
||||
QuotaStoreSettingsSchema,
|
||||
QuotaPreviewQuerySchema,
|
||||
AuditLogQuerySchema,
|
||||
} from "../../src/shared/schemas/quota";
|
||||
|
||||
test("PoolCreateSchema accepts valid input", () => {
|
||||
assert.ok(
|
||||
PoolCreateSchema.safeParse({ connectionId: "c", name: "Team Pool", allocations: [] }).success
|
||||
);
|
||||
});
|
||||
|
||||
test("PoolCreateSchema defaults allocations to []", () => {
|
||||
const r = PoolCreateSchema.safeParse({ connectionId: "c", name: "Pool" });
|
||||
assert.ok(r.success);
|
||||
assert.deepEqual(r.data?.allocations, []);
|
||||
});
|
||||
|
||||
test("PoolCreateSchema rejects empty name", () => {
|
||||
assert.equal(PoolCreateSchema.safeParse({ connectionId: "c", name: "" }).success, false);
|
||||
});
|
||||
|
||||
test("PoolCreateSchema rejects empty connectionId", () => {
|
||||
assert.equal(PoolCreateSchema.safeParse({ connectionId: "", name: "x" }).success, false);
|
||||
});
|
||||
|
||||
test("PoolCreateSchema rejects name > 120 chars", () => {
|
||||
assert.equal(
|
||||
PoolCreateSchema.safeParse({ connectionId: "c", name: "x".repeat(121) }).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test("PoolUpdateSchema accepts partial (only name)", () => {
|
||||
const r = PoolUpdateSchema.safeParse({ name: "New" });
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data?.name, "New");
|
||||
});
|
||||
|
||||
test("PoolUpdateSchema accepts empty object (no-op)", () => {
|
||||
assert.ok(PoolUpdateSchema.safeParse({}).success);
|
||||
});
|
||||
|
||||
test("PoolUpdateSchema rejects empty name when provided", () => {
|
||||
assert.equal(PoolUpdateSchema.safeParse({ name: "" }).success, false);
|
||||
});
|
||||
|
||||
test("PlanUpsertSchema accepts valid dimensions array", () => {
|
||||
assert.ok(
|
||||
PlanUpsertSchema.safeParse({ dimensions: [{ unit: "percent", window: "5h", limit: 100 }] })
|
||||
.success
|
||||
);
|
||||
});
|
||||
|
||||
test("PlanUpsertSchema rejects empty dimensions array", () => {
|
||||
assert.equal(PlanUpsertSchema.safeParse({ dimensions: [] }).success, false);
|
||||
});
|
||||
|
||||
test("PlanUpsertSchema accepts multiple dimensions", () => {
|
||||
const r = PlanUpsertSchema.safeParse({
|
||||
dimensions: [
|
||||
{ unit: "percent", window: "5h", limit: 100 },
|
||||
{ unit: "percent", window: "weekly", limit: 100 },
|
||||
],
|
||||
});
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data?.dimensions.length, 2);
|
||||
});
|
||||
|
||||
test("QuotaStoreSettingsSchema accepts sqlite driver", () => {
|
||||
assert.ok(QuotaStoreSettingsSchema.safeParse({ driver: "sqlite" }).success);
|
||||
});
|
||||
|
||||
test("QuotaStoreSettingsSchema accepts redis driver with valid URL", () => {
|
||||
assert.ok(
|
||||
QuotaStoreSettingsSchema.safeParse({
|
||||
driver: "redis",
|
||||
redisUrl: "redis://localhost:6379",
|
||||
}).success
|
||||
);
|
||||
});
|
||||
|
||||
test("QuotaStoreSettingsSchema rejects malformed redisUrl", () => {
|
||||
assert.equal(
|
||||
QuotaStoreSettingsSchema.safeParse({ driver: "redis", redisUrl: "not-a-url" }).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test("QuotaStoreSettingsSchema accepts null redisUrl", () => {
|
||||
assert.ok(QuotaStoreSettingsSchema.safeParse({ driver: "sqlite", redisUrl: null }).success);
|
||||
});
|
||||
|
||||
test("QuotaStoreSettingsSchema rejects unknown driver", () => {
|
||||
assert.equal(QuotaStoreSettingsSchema.safeParse({ driver: "mysql" }).success, false);
|
||||
});
|
||||
|
||||
test("QuotaPreviewQuerySchema coerces string estimatedTokens to number", () => {
|
||||
const r = QuotaPreviewQuerySchema.safeParse({
|
||||
apiKeyId: "k",
|
||||
poolId: "p",
|
||||
estimatedTokens: "1500",
|
||||
});
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data?.estimatedTokens, 1500);
|
||||
});
|
||||
|
||||
test("QuotaPreviewQuerySchema rejects negative estimatedTokens", () => {
|
||||
assert.equal(
|
||||
QuotaPreviewQuerySchema.safeParse({
|
||||
apiKeyId: "k",
|
||||
poolId: "p",
|
||||
estimatedTokens: "-1",
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test("QuotaPreviewQuerySchema rejects empty apiKeyId", () => {
|
||||
assert.equal(QuotaPreviewQuerySchema.safeParse({ apiKeyId: "", poolId: "p" }).success, false);
|
||||
});
|
||||
|
||||
test("QuotaPreviewQuerySchema rejects empty poolId", () => {
|
||||
assert.equal(QuotaPreviewQuerySchema.safeParse({ apiKeyId: "k", poolId: "" }).success, false);
|
||||
});
|
||||
|
||||
test("AuditLogQuerySchema defaults level to 'all'", () => {
|
||||
const r = AuditLogQuerySchema.safeParse({});
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data?.level, "all");
|
||||
});
|
||||
|
||||
test("AuditLogQuerySchema accepts level=high", () => {
|
||||
const r = AuditLogQuerySchema.safeParse({ level: "high" });
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data?.level, "high");
|
||||
});
|
||||
|
||||
test("AuditLogQuerySchema rejects unknown level", () => {
|
||||
assert.equal(AuditLogQuerySchema.safeParse({ level: "medium" }).success, false);
|
||||
});
|
||||
|
||||
test("AuditLogQuerySchema defaults limit to 50", () => {
|
||||
const r = AuditLogQuerySchema.safeParse({});
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data?.limit, 50);
|
||||
});
|
||||
|
||||
test("AuditLogQuerySchema coerces string limit to number", () => {
|
||||
const r = AuditLogQuerySchema.safeParse({ limit: "100" });
|
||||
assert.ok(r.success);
|
||||
assert.equal(r.data?.limit, 100);
|
||||
});
|
||||
|
||||
test("AuditLogQuerySchema rejects limit=0", () => {
|
||||
assert.equal(AuditLogQuerySchema.safeParse({ limit: "0" }).success, false);
|
||||
});
|
||||
|
||||
test("AuditLogQuerySchema rejects limit > 500", () => {
|
||||
assert.equal(AuditLogQuerySchema.safeParse({ limit: "501" }).success, false);
|
||||
});
|
||||
Reference in New Issue
Block a user