feat(quota): group-aware quotaShared combos (qtSd/<group>/...) with provider-scoped prune

- resolvePoolForSync now resolves groupName via getGroupName(pool.groupId),
  falling back to pool.name when the group record is missing.
- Both quotaModelName calls in syncQuotaCombos use groupName instead of
  pool.name, so combos are named qtSd/<groupSlug>/<provider>/<model>.
- Prune is now scoped to group+provider: syncing pool A (openrouter) never
  deletes pool B (baidu) combos that share the same group.
- removeQuotaCombosForPool likewise scoped to group+provider.
- Updated quota-combos-sync, quota-combo-balancing, quota-combo-cli-providers
  tests for the new group-slug naming.
- New tests/unit/quota-combo-groups.test.ts: G1–G4 cover two-pool same-group
  naming, provider-scoped prune isolation, default-group fallback, and stale
  prune within the same group+provider.
This commit is contained in:
diegosouzapw
2026-05-31 19:02:32 -03:00
parent cb1a18fa1a
commit fc6692925e
5 changed files with 457 additions and 46 deletions

View File

@@ -11,6 +11,7 @@
*/
import { getPool } from "@/lib/db/quotaPools";
import { getGroupName } from "@/lib/db/quotaGroups";
import { getProviderConnectionById } from "@/lib/db/providers";
import {
getCombos,
@@ -20,7 +21,7 @@ import {
updateCombo,
} from "@/lib/db/combos";
import { REGISTRY } from "@omniroute/open-sse/config/providerRegistry";
import { quotaModelName, parseQuotaModelName, isQuotaModelName, quotaPoolSlug } from "./quotaModelNaming";
import { quotaModelName, parseQuotaModelName, isQuotaModelName, quotaGroupSlug } from "./quotaModelNaming";
import { createLogger } from "@/shared/utils/logger";
const log = createLogger("quota/quotaCombos");
@@ -34,9 +35,20 @@ const log = createLogger("quota/quotaCombos");
* Returns null when the pool cannot be found.
* Individual connection lookups are deferred to syncQuotaCombos so that a
* single missing connection does not abort the whole sync.
*
* B4: also resolves the GROUP NAME so that combos are named
* `qtSd/<groupSlug>/...` instead of `qtSd/<poolSlug>/...`.
* Falls back to pool.name when the group record is missing.
*/
async function resolvePoolForSync(poolId: string): Promise<{
pool: { id: string; connectionId: string; connectionIds: string[]; name: string };
pool: {
id: string;
connectionId: string;
connectionIds: string[];
name: string;
groupId: string;
groupName: string;
};
} | null> {
const pool = getPool(poolId);
if (!pool) return null;
@@ -47,7 +59,20 @@ async function resolvePoolForSync(poolId: string): Promise<{
? pool.connectionIds
: [pool.connectionId];
return { pool: { id: pool.id, connectionId: pool.connectionId, connectionIds, name: pool.name } };
// B4: resolve the group name for combo naming.
// Fall back to pool.name when the group is missing (legacy / test isolation).
const groupName = getGroupName(pool.groupId) ?? pool.name;
return {
pool: {
id: pool.id,
connectionId: pool.connectionId,
connectionIds,
name: pool.name,
groupId: pool.groupId,
groupName,
},
};
}
/**
@@ -94,7 +119,10 @@ export async function syncQuotaCombos(poolId: string): Promise<void> {
}
const { pool } = resolved;
const poolSlug = quotaPoolSlug(pool.name);
// B4: use the GROUP name for combo naming (qtSd/<groupSlug>/...).
// Falls back to pool.name when the group record is missing.
const groupName = pool.groupName;
const groupSlug = quotaGroupSlug(groupName);
// D2: build desired names as the UNION across ALL member connections.
// A missing connection (no DB row / no provider field) is silently skipped —
@@ -121,11 +149,17 @@ export async function syncQuotaCombos(poolId: string): Promise<void> {
if (modelIds.length === 0) continue;
for (const modelId of modelIds) {
desiredNames.add(quotaModelName(pool.name, provider, modelId));
// B4: use groupName (not pool.name) as the first arg so combos carry the group slug.
desiredNames.add(quotaModelName(groupName, provider, modelId));
}
upsertWork.push({ connId, provider, modelIds });
}
// B4: the pool is single-provider (guard enforced at pool creation).
// Compute the pool's provider so the prune is scoped to group+provider only
// (never touching another provider's combos in the same group).
const poolProvider: string | undefined = upsertWork[0]?.provider;
// Group steps by model across all connections (Task 3 guarantees a single provider).
// This produces one combo per model with ALL connections' steps + strategy "fill-first",
// fixing the collision where two same-provider connections would overwrite each other.
@@ -139,7 +173,8 @@ export async function syncQuotaCombos(poolId: string): Promise<void> {
}
for (const [modelId, conns] of byModel) {
const provider = conns[0].provider;
const comboName = quotaModelName(pool.name, provider, modelId);
// B4: use groupName for the combo name.
const comboName = quotaModelName(groupName, provider, modelId);
const steps = conns.map((c) => ({
kind: "model" as const,
model: `${provider}/${modelId}`,
@@ -157,8 +192,17 @@ export async function syncQuotaCombos(poolId: string): Promise<void> {
}
}
// Prune stale combos that belong to this pool slug but are no longer in the
// desired set (union across all current connections).
// B4: Prune stale combos that belong to THIS pool's group+provider but are no
// longer in the desired set. CRITICAL: must NOT prune another provider's combos
// in the same group (e.g. syncing openrouter pool must not delete baidu combos).
//
// Prune condition: groupSlug matches AND provider matches AND name not in desiredNames.
// If poolProvider is undefined (no valid connection), skip pruning to be safe.
if (!poolProvider) {
// No valid connections → nothing to prune (can't scope by provider).
return;
}
let allCombos: Awaited<ReturnType<typeof getCombos>> = [];
try {
allCombos = await getCombos();
@@ -174,9 +218,12 @@ export async function syncQuotaCombos(poolId: string): Promise<void> {
const parsed = parseQuotaModelName(name);
if (!parsed) continue;
if (parsed.groupSlug !== poolSlug) continue;
// Belongs to this pool slug but not produced by any current connection → prune.
// B4: provider-scoped prune — only prune combos for THIS group+provider.
if (parsed.groupSlug !== groupSlug) continue;
if (parsed.provider !== poolProvider) continue;
// Belongs to this group+provider but not produced by any current connection → prune.
if (!desiredNames.has(name)) {
try {
await deleteComboByName(name);
@@ -217,15 +264,46 @@ export function filterModelsToQuotaPools<T extends { id: string }>(
/**
* Delete ALL `quotaShared-*` combos that belong to the given pool.
*
* Used on pool deletion. Because the pool may already be gone from the DB when
* this is called, we look up the pool name first; if missing, we fall back to
* scanning all quota combos and deleting those whose parsed slug matches the
* pool's last-known slug (best-effort via poolId as slug).
* B4: scoped to this pool's group+provider so that removing one pool does not
* accidentally delete another provider's combos that share the same group.
*
* Used on pool deletion. The pool record is looked up to resolve the group
* name and provider. If the pool is already gone from the DB, this is a
* best-effort no-op (nothing to match on provider).
*/
export async function removeQuotaCombosForPool(poolId: string): Promise<void> {
// Try to get the pool's name to compute the canonical slug
const pool = getPool(poolId);
const slug = pool ? quotaPoolSlug(pool.name) : null;
// Resolve pool → groupName + provider for scoped deletion.
const resolved = await resolvePoolForSync(poolId);
// If the pool is already gone, we can't safely scope deletion.
// Fall back: try to delete by provider-discovery from DB combos (best-effort).
if (!resolved) {
// Pool gone — nothing to scope by; skip (no partial prune without knowing the provider).
return;
}
const { pool } = resolved;
const groupName = pool.groupName;
const groupSlug = quotaGroupSlug(groupName);
// Resolve the pool's provider from its connections.
let poolProvider: string | undefined;
for (const connId of pool.connectionIds) {
try {
const connection = (await getProviderConnectionById(connId)) as Record<string, unknown> | null;
if (connection && typeof connection.provider === "string" && connection.provider.length > 0) {
poolProvider = connection.provider;
break;
}
} catch {
// skip
}
}
if (!poolProvider) {
// Can't scope by provider — skip to avoid nuking unrelated combos.
return;
}
let allCombos: Awaited<ReturnType<typeof getCombos>> = [];
try {
@@ -243,8 +321,9 @@ export async function removeQuotaCombosForPool(poolId: string): Promise<void> {
const parsed = parseQuotaModelName(name);
if (!parsed) continue;
// Match by slug when we have a pool name; otherwise no match possible
if (slug !== null && parsed.groupSlug !== slug) continue;
// B4: only delete combos for this group+provider.
if (parsed.groupSlug !== groupSlug) continue;
if (parsed.provider !== poolProvider) continue;
try {
await deleteComboByName(name);

View File

@@ -122,7 +122,8 @@ test("B1: syncQuotaCombos — 2-connection same-provider pool produces ONE combo
// For each model, assert: one combo, 2 steps, fill-first, both connIds present.
for (const modelId of modelsForProvider) {
const comboName = quotaModelName(pool.name, PROVIDER, modelId);
// B4: combos are named with the GROUP name ("GroupDemo"), not pool name.
const comboName = quotaModelName("GroupDemo", PROVIDER, modelId);
const matchingCombos = quotaCombos.filter((c) => c.name === comboName);
// Exactly ONE combo with this name (no duplicate/collision).
@@ -332,7 +333,8 @@ test("B5: after syncQuotaCombos on 2-connection pool, getComboByName returns the
await syncQuotaCombos(pool.id);
const comboName = quotaModelName(pool.name, PROVIDER, FIRST_MODEL);
// B4: combos are named with the GROUP name ("GroupDemo"), not pool name.
const comboName = quotaModelName("GroupDemo", PROVIDER, FIRST_MODEL);
const found = await combosDb.getComboByName(comboName);
assert.ok(found, `getComboByName("${comboName}") should return the combo`);

View File

@@ -71,7 +71,8 @@ test("syncQuotaCombos generates qtSd/ combos for a CLI provider (codex) via REGI
);
assert.ok(quotaCombos.length > 0, "codex pool must produce qtSd/ combos (was 0 before fix)");
const expectedName = quotaModelName(pool.name, CLI_PROVIDER, firstModelId);
// B4: combos are named with the GROUP name ("GroupDemo" for default group), not pool name.
const expectedName = quotaModelName("GroupDemo", CLI_PROVIDER, firstModelId);
const combo = await combosDb.getComboByName(expectedName);
assert.ok(combo, `combo "${expectedName}" must exist for the codex pool`);
});

View File

@@ -0,0 +1,303 @@
/**
* tests/unit/quota-combo-groups.test.ts
*
* Task B4 TDD — Group-aware quotaShared combos.
*
* Two pools in the SAME group produce combos under the GROUP slug
* (`qtSd/<group>/...`) and each pool's sync does NOT prune the other
* provider's combos.
*
* Uses "openrouter" (1 model: "auto") as pool A's provider and "baidu"
* (1 model: "ernie-4.0-8k") as pool B's provider — both have a small,
* stable registry entry.
*/
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-quota-combo-groups-")
);
process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const poolsDb = await import("../../src/lib/db/quotaPools.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const combosDb = await import("../../src/lib/db/combos.ts");
const { createGroup } = await import("../../src/lib/db/quotaGroups.ts");
const { syncQuotaCombos } = await import("../../src/lib/quota/quotaCombos.ts");
const { isQuotaModelName, parseQuotaModelName, quotaGroupSlug } = await import(
"../../src/lib/quota/quotaModelNaming.ts"
);
// ---------------------------------------------------------------------------
// Lifecycle
// ---------------------------------------------------------------------------
async function resetStorage() {
core.resetDbInstance();
for (let attempt = 0; attempt < 10; attempt++) {
try {
if (fs.existsSync(TEST_DATA_DIR)) {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
}
break;
} catch (error: unknown) {
const err = error as NodeJS.ErrnoException;
if ((err?.code === "EBUSY" || err?.code === "EPERM") && attempt < 9) {
await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
} else {
throw error;
}
}
}
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.beforeEach(async () => {
await resetStorage();
});
test.after(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
// ---------------------------------------------------------------------------
// Helper
// ---------------------------------------------------------------------------
async function listQuotaCombos(): Promise<Array<{ name: string; models: unknown[] }>> {
const all = await combosDb.getCombos();
return all
.filter((c) => typeof c.name === "string" && isQuotaModelName(c.name as string))
.map((c) => ({
name: c.name as string,
models: Array.isArray(c.models) ? (c.models as unknown[]) : [],
}));
}
// ---------------------------------------------------------------------------
// G1 — Two pools in the SAME group produce combos under the GROUP slug
// ---------------------------------------------------------------------------
test("G1: two pools in same group → combos named qtSd/<group>/provider/model (group slug, not pool name)", async () => {
// Create a named group
const group = createGroup("MyGroup");
// Pool A: openrouter
const connA = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "g1-or-conn",
apiKey: "sk-g1-or",
});
const idA = (connA as Record<string, unknown>).id as string;
const poolA = poolsDb.createPool({
connectionId: idA,
name: "OpenRouter Pool G1",
groupId: group.id,
});
// Pool B: baidu
const connB = await providersDb.createProviderConnection({
provider: "baidu",
authType: "apikey",
name: "g1-baidu-conn",
apiKey: "sk-g1-baidu",
});
const idB = (connB as Record<string, unknown>).id as string;
const poolB = poolsDb.createPool({
connectionId: idB,
name: "Baidu Pool G1",
groupId: group.id,
});
// Sync both pools
await syncQuotaCombos(poolA.id);
await syncQuotaCombos(poolB.id);
const allCombos = await listQuotaCombos();
const groupSlug = quotaGroupSlug(group.name); // "mygroup"
// All combos should use the group slug, not the pool name slug
for (const c of allCombos) {
const parsed = parseQuotaModelName(c.name);
assert.ok(parsed, `Could not parse quota model name: ${c.name}`);
assert.equal(
parsed.groupSlug,
groupSlug,
`Combo "${c.name}" groupSlug should be "${groupSlug}" (group name), got "${parsed.groupSlug}"`
);
}
// Combos for openrouter must exist under the group slug
const orCombos = allCombos.filter((c) => {
const p = parseQuotaModelName(c.name);
return p?.groupSlug === groupSlug && p?.provider === "openrouter";
});
assert.ok(orCombos.length > 0, `Expected openrouter combos under qtSd/${groupSlug}/openrouter/...`);
// Combos for baidu must exist under the group slug
const baiduCombos = allCombos.filter((c) => {
const p = parseQuotaModelName(c.name);
return p?.groupSlug === groupSlug && p?.provider === "baidu";
});
assert.ok(baiduCombos.length > 0, `Expected baidu combos under qtSd/${groupSlug}/baidu/...`);
});
// ---------------------------------------------------------------------------
// G2 — Re-syncing pool A does NOT prune pool B's combos (provider-scoped prune)
// ---------------------------------------------------------------------------
test("G2: re-syncing pool A (openrouter) does not delete pool B (baidu) combos in same group", async () => {
const group = createGroup("SharedGroup");
const groupSlug = quotaGroupSlug(group.name);
// Pool A: openrouter
const connA = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "g2-or-conn",
apiKey: "sk-g2-or",
});
const idA = (connA as Record<string, unknown>).id as string;
const poolA = poolsDb.createPool({
connectionId: idA,
name: "OpenRouter Pool G2",
groupId: group.id,
});
// Pool B: baidu
const connB = await providersDb.createProviderConnection({
provider: "baidu",
authType: "apikey",
name: "g2-baidu-conn",
apiKey: "sk-g2-baidu",
});
const idB = (connB as Record<string, unknown>).id as string;
const poolB = poolsDb.createPool({
connectionId: idB,
name: "Baidu Pool G2",
groupId: group.id,
});
// Sync both
await syncQuotaCombos(poolA.id);
await syncQuotaCombos(poolB.id);
// Count baidu combos before re-sync of pool A
const beforeResync = await listQuotaCombos();
const baiduBefore = beforeResync.filter((c) => {
const p = parseQuotaModelName(c.name);
return p?.groupSlug === groupSlug && p?.provider === "baidu";
});
assert.ok(baiduBefore.length > 0, "baidu combos must exist before re-sync");
// Re-sync pool A (openrouter) again — must NOT touch baidu combos
await syncQuotaCombos(poolA.id);
const afterResync = await listQuotaCombos();
const baiduAfter = afterResync.filter((c) => {
const p = parseQuotaModelName(c.name);
return p?.groupSlug === groupSlug && p?.provider === "baidu";
});
assert.equal(
baiduAfter.length,
baiduBefore.length,
`Re-syncing pool A (openrouter) must not prune pool B's (baidu) combos. ` +
`Before: ${baiduBefore.length}, After: ${baiduAfter.length}`
);
// Also verify openrouter combos still present
const orAfter = afterResync.filter((c) => {
const p = parseQuotaModelName(c.name);
return p?.groupSlug === groupSlug && p?.provider === "openrouter";
});
assert.ok(orAfter.length > 0, "openrouter combos must still be present after re-sync");
});
// ---------------------------------------------------------------------------
// G3 — Pool in default group still works (group name = "GroupDemo", slug = "groupdemo")
// ---------------------------------------------------------------------------
test("G3: pool in default 'group-demo' group produces combos under groupdemo slug", async () => {
// Create a pool without specifying a groupId → defaults to "group-demo"
const conn = await providersDb.createProviderConnection({
provider: "glm",
authType: "apikey",
name: "g3-glm-conn",
apiKey: "sk-g3-glm",
});
const connId = (conn as Record<string, unknown>).id as string;
const pool = poolsDb.createPool({
connectionId: connId,
name: "Default Group Pool",
// no groupId → defaults to "group-demo"
});
// Sanity: groupId should be "group-demo"
assert.equal(pool.groupId, "group-demo", "pool without groupId should default to group-demo");
await syncQuotaCombos(pool.id);
const allCombos = await listQuotaCombos();
assert.ok(allCombos.length > 0, "should produce combos even for default group pool");
// All combos should be under the "groupdemo" slug (the group name is "GroupDemo")
for (const c of allCombos) {
const parsed = parseQuotaModelName(c.name);
assert.ok(parsed, `Could not parse: ${c.name}`);
assert.equal(
parsed.groupSlug,
"groupdemo",
`Combo "${c.name}" should be under "groupdemo" slug (GroupDemo group), got "${parsed.groupSlug}"`
);
}
});
// ---------------------------------------------------------------------------
// G4 — Stale prune: a same-group same-provider stale combo IS pruned on re-sync
// ---------------------------------------------------------------------------
test("G4: stale same-group same-provider combo is pruned on re-sync", async () => {
const group = createGroup("PruneGroup");
const groupSlug = quotaGroupSlug(group.name); // "prunegroup"
const conn = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "g4-or-conn",
apiKey: "sk-g4-or",
});
const connId = (conn as Record<string, unknown>).id as string;
const pool = poolsDb.createPool({
connectionId: connId,
name: "PrunePool G4",
groupId: group.id,
});
await syncQuotaCombos(pool.id);
// Manually insert a stale combo under same group+provider but a nonexistent model
const staleComboName = `qtSd/${groupSlug}/openrouter/fake-stale-model`;
await combosDb.createCombo({
name: staleComboName,
models: [{ kind: "model", model: "openrouter/fake-stale-model", providerId: "openrouter", weight: 100 }],
strategy: "priority",
isHidden: true,
});
const beforePrune = await combosDb.getComboByName(staleComboName);
assert.ok(beforePrune, "stale combo should exist before re-sync");
// Re-sync: stale same-group same-provider combo should be pruned
await syncQuotaCombos(pool.id);
const afterPrune = await combosDb.getComboByName(staleComboName);
assert.equal(afterPrune, null, "stale same-group same-provider combo should be pruned");
});

View File

@@ -92,6 +92,7 @@ test("syncQuotaCombos: creates one combo per glm model with correct name and tar
const connId = (conn as Record<string, unknown>).id as string;
assert.ok(connId, "connection should have an id");
// Pool defaults to "group-demo" (GroupDemo → slug "groupdemo").
const pool = poolsDb.createPool({ connectionId: connId, name: "TestGlmPool" });
await syncQuotaCombos(pool.id);
@@ -102,20 +103,21 @@ test("syncQuotaCombos: creates one combo per glm model with correct name and tar
const quotaCombos = await listQuotaCombos();
const quotaComboNames = new Set(quotaCombos.map((c) => c.name));
// Every glm model should have a combo
// B4: combos are named with the GROUP name ("GroupDemo" → slug "groupdemo"), not pool name.
const expectedGroupSlug = quotaPoolSlug("GroupDemo");
for (const model of glmModels) {
const expectedName = quotaModelName("TestGlmPool", "glm", model.id);
const expectedName = quotaModelName("GroupDemo", "glm", model.id);
assert.ok(
quotaComboNames.has(expectedName),
`Missing combo for model ${model.id}: ${expectedName}`
);
}
// No extra quota combos for other pools
// All combos should be under the group slug (not the pool name slug).
for (const c of quotaCombos) {
const parsed = parseQuotaModelName(c.name);
assert.ok(parsed, `Could not parse quota model name: ${c.name}`);
assert.equal(parsed?.groupSlug, quotaPoolSlug("TestGlmPool"));
assert.equal(parsed?.groupSlug, expectedGroupSlug);
}
});
@@ -203,9 +205,9 @@ test("syncQuotaCombos: prunes stale combos for same pool slug", async () => {
const initialCount = afterInitial.length;
assert.ok(initialCount > 0, "should have combos after initial sync");
// Manually insert a stale combo with the same pool slug but a nonexistent model
// Use the new qtSd/ prefix so isQuotaModelName() recognises it as a quota combo to prune.
const staleComboName = `qtSd/${quotaPoolSlug("PrunePool")}/glm/fake-model-stale`;
// Manually insert a stale combo with the same group+provider slug but a nonexistent model.
// B4: prune is group+provider scoped. Pool defaults to "group-demo" (GroupDemo → "groupdemo").
const staleComboName = `qtSd/${quotaPoolSlug("GroupDemo")}/glm/fake-model-stale`;
await combosDb.createCombo({
name: staleComboName,
models: [{ kind: "model", model: "glm/fake-model-stale", providerId: "glm", weight: 100 }],
@@ -253,40 +255,64 @@ test("removeQuotaCombosForPool: removes all quota combos for the pool", async ()
assert.equal(after.length, 0, "all quota combos should be removed");
});
test("syncQuotaCombos: does not affect quota combos for a different pool slug", async () => {
const conn = await providersDb.createProviderConnection({
test("syncQuotaCombos: does not affect quota combos for a different provider in the same group", async () => {
// B4: isolation is now by group+provider (not pool name slug).
// Two pools in the same group (group-demo) but different providers:
// PoolAlpha = glm, PoolBeta = openrouter. Removing PoolAlpha (glm) should
// NOT touch PoolBeta's (openrouter) combos.
const connGlm = await providersDb.createProviderConnection({
provider: "glm",
authType: "apikey",
name: "quota-combos-isolation",
name: "quota-combos-isolation-glm",
apiKey: "sk-test-glm-isolation",
});
const connId = (conn as Record<string, unknown>).id as string;
const connGlmId = (connGlm as Record<string, unknown>).id as string;
const poolA = poolsDb.createPool({ connectionId: connId, name: "PoolAlpha" });
const poolB = poolsDb.createPool({ connectionId: connId, name: "PoolBeta" });
const connOr = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "quota-combos-isolation-or",
apiKey: "sk-test-or-isolation",
});
const connOrId = (connOr as Record<string, unknown>).id as string;
// Both pools default to "group-demo" (same group).
const poolA = poolsDb.createPool({ connectionId: connGlmId, name: "PoolAlpha" });
const poolB = poolsDb.createPool({ connectionId: connOrId, name: "PoolBeta" });
await syncQuotaCombos(poolA.id);
await syncQuotaCombos(poolB.id);
const all = await listQuotaCombos();
const slugA = quotaPoolSlug("PoolAlpha");
const slugB = quotaPoolSlug("PoolBeta");
const groupSlug = quotaPoolSlug("GroupDemo");
const forA = all.filter((c) => parseQuotaModelName(c.name)?.groupSlug === slugA);
const forB = all.filter((c) => parseQuotaModelName(c.name)?.groupSlug === slugB);
const forA = all.filter((c) => {
const p = parseQuotaModelName(c.name);
return p?.groupSlug === groupSlug && p?.provider === "glm";
});
const forB = all.filter((c) => {
const p = parseQuotaModelName(c.name);
return p?.groupSlug === groupSlug && p?.provider === "openrouter";
});
assert.ok(forA.length > 0, "PoolAlpha should have combos");
assert.ok(forB.length > 0, "PoolBeta should have combos");
assert.ok(forA.length > 0, "PoolAlpha (glm) should have combos");
assert.ok(forB.length > 0, "PoolBeta (openrouter) should have combos");
// Removing PoolA's combos should not touch PoolB's
// Removing PoolAlpha (glm) combos should NOT touch PoolBeta's (openrouter) combos.
await removeQuotaCombosForPool(poolA.id);
const remaining = await listQuotaCombos();
const remainingForA = remaining.filter((c) => parseQuotaModelName(c.name)?.groupSlug === slugA);
const remainingForB = remaining.filter((c) => parseQuotaModelName(c.name)?.groupSlug === slugB);
const remainingForA = remaining.filter((c) => {
const p = parseQuotaModelName(c.name);
return p?.groupSlug === groupSlug && p?.provider === "glm";
});
const remainingForB = remaining.filter((c) => {
const p = parseQuotaModelName(c.name);
return p?.groupSlug === groupSlug && p?.provider === "openrouter";
});
assert.equal(remainingForA.length, 0, "PoolAlpha combos should all be removed");
assert.equal(remainingForB.length, forB.length, "PoolBeta combos should be untouched");
assert.equal(remainingForA.length, 0, "PoolAlpha (glm) combos should all be removed");
assert.equal(remainingForB.length, forB.length, "PoolBeta (openrouter) combos should be untouched");
});
test("syncQuotaCombos: unknown pool id — no throw, prunes nothing (no combos exist)", async () => {