diff --git a/src/server/routes/api/accounts.manual-models.test.ts b/src/server/routes/api/accounts.manual-models.test.ts index c2caae4..23aa561 100644 --- a/src/server/routes/api/accounts.manual-models.test.ts +++ b/src/server/routes/api/accounts.manual-models.test.ts @@ -130,4 +130,16 @@ describe('accounts manual models endpoint', () => { expect(response.statusCode).toBe(404); }); + + it('returns validation error for empty models array', async () => { + const response = await app.inject({ + method: 'POST', + url: '/api/accounts/1/models/manual', + payload: { + models: [], + }, + }); + + expect(response.statusCode).toBe(400); + }); }); diff --git a/src/server/routes/api/accounts.skip-model-fetch.test.ts b/src/server/routes/api/accounts.skip-model-fetch.test.ts index b0ce3dd..d8b6bd1 100644 --- a/src/server/routes/api/accounts.skip-model-fetch.test.ts +++ b/src/server/routes/api/accounts.skip-model-fetch.test.ts @@ -1,6 +1,6 @@ import Fastify, { type FastifyInstance } from 'fastify'; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; -import { mkdtempSync } from 'node:fs'; +import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; @@ -50,6 +50,11 @@ describe('accounts skipModelFetch behavior', () => { afterAll(async () => { await app.close(); + if (dataDir) { + try { + rmSync(dataDir, { recursive: true, force: true }); + } catch { } + } delete process.env.DATA_DIR; }); diff --git a/src/server/routes/api/accounts.ts b/src/server/routes/api/accounts.ts index 341058c..ebaa94f 100644 --- a/src/server/routes/api/accounts.ts +++ b/src/server/routes/api/accounts.ts @@ -1432,30 +1432,32 @@ export async function accountsRoutes(app: FastifyInstance) { } try { - for (const modelName of normalizedModels) { - const existing = await db.select() - .from(schema.modelAvailability) - .where(and(eq(schema.modelAvailability.accountId, accountId), eq(schema.modelAvailability.modelName, modelName))) - .get(); + await db.transaction(async (tx: any) => { + for (const modelName of normalizedModels) { + const existing = await tx.select() + .from(schema.modelAvailability) + .where(and(eq(schema.modelAvailability.accountId, accountId), eq(schema.modelAvailability.modelName, modelName))) + .get(); - if (existing) { - if (!existing.available || !existing.isManual) { - await db.update(schema.modelAvailability) - .set({ available: true, latencyMs: null, isManual: true, checkedAt: new Date().toISOString() }) - .where(eq(schema.modelAvailability.id, existing.id)) - .run(); + if (existing) { + if (!existing.available || !existing.isManual) { + await tx.update(schema.modelAvailability) + .set({ available: true, latencyMs: null, isManual: true, checkedAt: new Date().toISOString() }) + .where(eq(schema.modelAvailability.id, existing.id)) + .run(); + } + } else { + await tx.insert(schema.modelAvailability).values({ + accountId, + modelName, + available: true, + isManual: true, + latencyMs: null, + checkedAt: new Date().toISOString(), + }).run(); } - } else { - await db.insert(schema.modelAvailability).values({ - accountId, - modelName, - available: true, - isManual: true, - latencyMs: null, - checkedAt: new Date().toISOString(), - }).run(); } - } + }); try { await rebuildTokenRoutesFromAvailability(); diff --git a/src/server/routes/proxy/models.test.ts b/src/server/routes/proxy/models.test.ts index 158e2b5..fad0be3 100644 --- a/src/server/routes/proxy/models.test.ts +++ b/src/server/routes/proxy/models.test.ts @@ -354,7 +354,7 @@ describe('/v1/models route', () => { await db.insert(schema.downstreamApiKeys).values({ name: 'search-key', - key: 'change-me-proxy-sk-token', + key: 'sk-search-key', enabled: true, }).run(); @@ -362,7 +362,7 @@ describe('/v1/models route', () => { method: 'GET', url: '/v1/models', headers: { - authorization: 'Bearer change-me-proxy-sk-token', + authorization: 'Bearer sk-search-key', }, });