forked from boos/BoosAPI
Add comprehensive tests for Anthropics and Gemini transformers
- Implement tests for anthropicMessagesTransformer including serialization and event handling in stream.test.ts. - Add usage extraction tests for anthropicMessagesUsage in usage.test.ts. - Create extensive tests for Gemini generate content aggregation and outbound usage in aggregator-outbound-usage.test.ts. - Introduce tests for Gemini stream handling and payload parsing in stream.test.ts. - Enhance OpenAI response serialization tests to ensure proper mapping of usage details and preservation of annotations in outbound.test.ts.
This commit is contained in:
@@ -40,6 +40,51 @@ describe('rebuildTokenRoutesFromAvailability', () => {
|
||||
delete process.env.DATA_DIR;
|
||||
});
|
||||
|
||||
it('creates an exact route with an account-direct channel for apikey model availability', async () => {
|
||||
const site = await db.insert(schema.sites).values({
|
||||
name: 'apikey-site',
|
||||
url: 'https://apikey-site.example.com',
|
||||
platform: 'new-api',
|
||||
}).returning().get();
|
||||
|
||||
const account = await db.insert(schema.accounts).values({
|
||||
siteId: site.id,
|
||||
username: 'apikey-user',
|
||||
accessToken: '',
|
||||
apiToken: 'sk-apikey-route',
|
||||
status: 'active',
|
||||
extraConfig: JSON.stringify({ credentialMode: 'apikey' }),
|
||||
}).returning().get();
|
||||
|
||||
await db.insert(schema.modelAvailability).values({
|
||||
accountId: account.id,
|
||||
modelName: 'gpt-5.2-codex',
|
||||
available: true,
|
||||
latencyMs: 1200,
|
||||
checkedAt: '2026-03-08T08:00:00.000Z',
|
||||
}).run();
|
||||
|
||||
const rebuild = await rebuildTokenRoutesFromAvailability();
|
||||
|
||||
expect(rebuild.models).toBe(1);
|
||||
|
||||
const route = await db.select().from(schema.tokenRoutes)
|
||||
.where(eq(schema.tokenRoutes.modelPattern, 'gpt-5.2-codex'))
|
||||
.get();
|
||||
expect(route).toBeDefined();
|
||||
|
||||
const channels = await db.select().from(schema.routeChannels)
|
||||
.where(and(
|
||||
eq(schema.routeChannels.routeId, route!.id),
|
||||
eq(schema.routeChannels.accountId, account.id),
|
||||
))
|
||||
.all();
|
||||
|
||||
expect(channels).toHaveLength(1);
|
||||
expect(channels[0]?.tokenId ?? null).toBeNull();
|
||||
expect(channels[0]?.manualOverride).toBe(false);
|
||||
});
|
||||
|
||||
it('removes stale exact routes and keeps wildcard routes on rebuild', async () => {
|
||||
const site = await db.insert(schema.sites).values({
|
||||
name: 'site-1',
|
||||
|
||||
@@ -250,8 +250,8 @@ async function refreshModelsForAllActiveAccounts() {
|
||||
return results;
|
||||
}
|
||||
|
||||
export async function rebuildTokenRoutesFromAvailability() {
|
||||
const tokenRows = await db.select().from(schema.tokenModelAvailability)
|
||||
export async function rebuildTokenRoutesFromAvailability() {
|
||||
const tokenRows = await db.select().from(schema.tokenModelAvailability)
|
||||
.innerJoin(schema.accountTokens, eq(schema.tokenModelAvailability.tokenId, schema.accountTokens.id))
|
||||
.innerJoin(schema.accounts, eq(schema.accountTokens.accountId, schema.accounts.id))
|
||||
.innerJoin(schema.sites, eq(schema.accounts.siteId, schema.sites.id))
|
||||
@@ -262,25 +262,49 @@ export async function rebuildTokenRoutesFromAvailability() {
|
||||
eq(schema.accounts.status, 'active'),
|
||||
eq(schema.sites.status, 'active'),
|
||||
),
|
||||
)
|
||||
.all();
|
||||
|
||||
const modelTokens = new Map<string, Map<number, number>>();
|
||||
for (const row of tokenRows) {
|
||||
const modelName = row.token_model_availability.modelName;
|
||||
if (!modelTokens.has(modelName)) modelTokens.set(modelName, new Map<number, number>());
|
||||
modelTokens.get(modelName)!.set(row.account_tokens.id, row.accounts.id);
|
||||
}
|
||||
|
||||
const routes = await db.select().from(schema.tokenRoutes).all();
|
||||
const channels = await db.select().from(schema.routeChannels).all();
|
||||
)
|
||||
.all();
|
||||
|
||||
const accountRows = await db.select().from(schema.modelAvailability)
|
||||
.innerJoin(schema.accounts, eq(schema.modelAvailability.accountId, schema.accounts.id))
|
||||
.innerJoin(schema.sites, eq(schema.accounts.siteId, schema.sites.id))
|
||||
.where(
|
||||
and(
|
||||
eq(schema.modelAvailability.available, true),
|
||||
eq(schema.accounts.status, 'active'),
|
||||
eq(schema.sites.status, 'active'),
|
||||
),
|
||||
)
|
||||
.all();
|
||||
|
||||
const modelCandidates = new Map<string, Map<string, { accountId: number; tokenId: number | null }>>();
|
||||
const addModelCandidate = (modelNameRaw: string | null | undefined, accountId: number, tokenId: number | null) => {
|
||||
const modelName = (modelNameRaw || '').trim();
|
||||
if (!modelName) return;
|
||||
if (!modelCandidates.has(modelName)) modelCandidates.set(modelName, new Map());
|
||||
const candidateKey = `${accountId}:${tokenId ?? 'account'}`;
|
||||
modelCandidates.get(modelName)!.set(candidateKey, { accountId, tokenId });
|
||||
};
|
||||
|
||||
for (const row of tokenRows) {
|
||||
addModelCandidate(row.token_model_availability.modelName, row.accounts.id, row.account_tokens.id);
|
||||
}
|
||||
|
||||
for (const row of accountRows) {
|
||||
if (!isApiKeyConnection(row.accounts)) continue;
|
||||
if (!(row.accounts.apiToken || '').trim()) continue;
|
||||
addModelCandidate(row.model_availability.modelName, row.accounts.id, null);
|
||||
}
|
||||
|
||||
const routes = await db.select().from(schema.tokenRoutes).all();
|
||||
const channels = await db.select().from(schema.routeChannels).all();
|
||||
|
||||
let createdRoutes = 0;
|
||||
let createdChannels = 0;
|
||||
let removedChannels = 0;
|
||||
let removedRoutes = 0;
|
||||
|
||||
for (const [modelName, tokenAccountMap] of modelTokens.entries()) {
|
||||
let createdChannels = 0;
|
||||
let removedChannels = 0;
|
||||
let removedRoutes = 0;
|
||||
|
||||
for (const [modelName, candidateMap] of modelCandidates.entries()) {
|
||||
let route = routes.find((r) => r.modelPattern === modelName);
|
||||
if (!route) {
|
||||
const inserted = await db.insert(schema.tokenRoutes).values({
|
||||
@@ -295,18 +319,21 @@ export async function rebuildTokenRoutesFromAvailability() {
|
||||
routes.push(route);
|
||||
createdRoutes++;
|
||||
}
|
||||
|
||||
const routeChannels = channels.filter((channel) => channel.routeId === route.id);
|
||||
const desiredTokenIds = new Set<number>(Array.from(tokenAccountMap.keys()));
|
||||
|
||||
for (const [tokenId, accountId] of tokenAccountMap.entries()) {
|
||||
const exists = routeChannels.some((channel) => channel.tokenId === tokenId);
|
||||
if (exists) continue;
|
||||
|
||||
|
||||
const routeChannels = channels.filter((channel) => channel.routeId === route.id);
|
||||
const desiredKeys = new Set(Array.from(candidateMap.keys()));
|
||||
|
||||
for (const [candidateKey, candidate] of candidateMap.entries()) {
|
||||
const exists = routeChannels.some((channel) => (
|
||||
channel.accountId === candidate.accountId
|
||||
&& (channel.tokenId ?? null) === candidate.tokenId
|
||||
));
|
||||
if (exists) continue;
|
||||
|
||||
const inserted = await db.insert(schema.routeChannels).values({
|
||||
routeId: route.id,
|
||||
accountId,
|
||||
tokenId,
|
||||
accountId: candidate.accountId,
|
||||
tokenId: candidate.tokenId,
|
||||
priority: 0,
|
||||
weight: 10,
|
||||
enabled: true,
|
||||
@@ -318,32 +345,34 @@ export async function rebuildTokenRoutesFromAvailability() {
|
||||
if (!created) continue;
|
||||
channels.push(created);
|
||||
createdChannels++;
|
||||
}
|
||||
|
||||
for (const channel of routeChannels) {
|
||||
if (channel.tokenId && desiredTokenIds.has(channel.tokenId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!channel.tokenId) {
|
||||
desiredKeys.add(candidateKey);
|
||||
}
|
||||
|
||||
for (const channel of routeChannels) {
|
||||
const channelKey = `${channel.accountId}:${channel.tokenId ?? 'account'}`;
|
||||
if (desiredKeys.has(channelKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!channel.tokenId) {
|
||||
const preferred = await getPreferredAccountToken(channel.accountId);
|
||||
if (preferred && desiredTokenIds.has(preferred.id)) {
|
||||
await db.update(schema.routeChannels)
|
||||
.set({ tokenId: preferred.id })
|
||||
.where(eq(schema.routeChannels.id, channel.id))
|
||||
.run();
|
||||
continue;
|
||||
if (preferred && desiredKeys.has(`${channel.accountId}:${preferred.id}`)) {
|
||||
await db.update(schema.routeChannels)
|
||||
.set({ tokenId: preferred.id })
|
||||
.where(eq(schema.routeChannels.id, channel.id))
|
||||
.run();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!channel.manualOverride) {
|
||||
await db.delete(schema.routeChannels).where(eq(schema.routeChannels.id, channel.id)).run();
|
||||
removedChannels++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const latestModelNames = new Set<string>(Array.from(modelTokens.keys()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const latestModelNames = new Set<string>(Array.from(modelCandidates.keys()));
|
||||
for (const route of routes) {
|
||||
const modelPattern = (route.modelPattern || '').trim();
|
||||
if (!modelPattern || !isExactModelPattern(modelPattern) || latestModelNames.has(modelPattern)) {
|
||||
@@ -367,13 +396,13 @@ export async function rebuildTokenRoutesFromAvailability() {
|
||||
|
||||
invalidateTokenRouterCache();
|
||||
|
||||
return {
|
||||
models: modelTokens.size,
|
||||
createdRoutes,
|
||||
createdChannels,
|
||||
removedChannels,
|
||||
removedRoutes,
|
||||
};
|
||||
return {
|
||||
models: modelCandidates.size,
|
||||
createdRoutes,
|
||||
createdChannels,
|
||||
removedChannels,
|
||||
removedRoutes,
|
||||
};
|
||||
}
|
||||
|
||||
export async function refreshModelsAndRebuildRoutes() {
|
||||
|
||||
Reference in New Issue
Block a user