From 258c676df4732fab0c800cc88d7dc90d174ca5e4 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Wed, 27 May 2026 19:05:06 -0300 Subject: [PATCH] test(quota): cover dimensions, schemas, plan registry --- tests/unit/quota-dimensions.test.ts | 203 +++++++++++++++++++++++++ tests/unit/quota-plan-registry.test.ts | 82 ++++++++++ tests/unit/quota-schemas.test.ts | 167 ++++++++++++++++++++ 3 files changed, 452 insertions(+) create mode 100644 tests/unit/quota-dimensions.test.ts create mode 100644 tests/unit/quota-plan-registry.test.ts create mode 100644 tests/unit/quota-schemas.test.ts diff --git a/tests/unit/quota-dimensions.test.ts b/tests/unit/quota-dimensions.test.ts new file mode 100644 index 0000000000..906f86a2e5 --- /dev/null +++ b/tests/unit/quota-dimensions.test.ts @@ -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(); + 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); +}); diff --git a/tests/unit/quota-plan-registry.test.ts b/tests/unit/quota-plan-registry.test.ts new file mode 100644 index 0000000000..e8fe2efcce --- /dev/null +++ b/tests/unit/quota-plan-registry.test.ts @@ -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`); + } +}); diff --git a/tests/unit/quota-schemas.test.ts b/tests/unit/quota-schemas.test.ts new file mode 100644 index 0000000000..85448c1505 --- /dev/null +++ b/tests/unit/quota-schemas.test.ts @@ -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); +});