import type Database from "bun:sqlite"; import { describe, expect, test } from "bun:test"; import type { Model, RuntimeMode } from "../../../src/shared/api"; import { createMigratedMemoryTestDatabase } from "../../helpers"; const MODE: RuntimeMode = "test"; async function createModelViaHandler(req: Request, db: Database): Promise { const { handleCreateModel: h } = await import("../../../src/server/routes/models/create"); return h(req, db, MODE); } function createTestModel(db: Database, pName: string, providerId?: string): Model { const pid = providerId ?? seedProvider(db); const result = createModel(db, { capabilities: ["text"], modelId: pName.toLowerCase().replace(/[^a-z0-9-]/g, "-"), name: pName, providerId: pid, }); if ("error" in result) throw new Error(result.error); return result.model; } async function deleteModelViaHandler(req: Request, db: Database): Promise { const { handleDeleteModel: h } = await import("../../../src/server/routes/models/delete"); return h(req, db, MODE); } async function disableModelViaHandler(req: Request, db: Database): Promise { const { handleDisableModel: h } = await import("../../../src/server/routes/models/disable"); return h(req, db, MODE); } async function enableModelViaHandler(req: Request, db: Database): Promise { const { handleEnableModel: h } = await import("../../../src/server/routes/models/enable"); return h(req, db, MODE); } async function getModelViaHandler(req: Request, db: Database): Promise { const { handleGetModel: h } = await import("../../../src/server/routes/models/get"); return h(req, db, MODE); } async function listModelsViaHandler(req: Request, db: Database): Promise { const { handleListModels: h } = await import("../../../src/server/routes/models/list"); return h(req, db, MODE); } import { createModel } from "../../../src/server/db/models"; import { createProvider } from "../../../src/server/db/providers"; function seedProvider(db: Database, name?: string): string { const result = createProvider(db, { apiKey: "sk-test", baseUrl: "https://api.test.com/v1", name: name ?? "TestProvider", type: "openai", }); if ("error" in result) throw new Error(result.error); return result.provider.id; } async function updateModelViaHandler(req: Request, db: Database): Promise { const { handleUpdateModel: h } = await import("../../../src/server/routes/models/update"); return h(req, db, MODE); } async function withRouteDb(callback: (db: Database) => Promise): Promise { const handle = createMigratedMemoryTestDatabase("route-model-test"); try { await callback(handle.db); handle.close(); } finally { handle.cleanup(); } } describe("models API routes", () => { test("POST /api/models create", async () => { await withRouteDb(async (db) => { const providerId = seedProvider(db); const req = new Request("http://localhost/api/models", { body: JSON.stringify({ capabilities: ["text", "reasoning"], modelId: "gpt-4o", name: "GPT-4o", providerId, }), headers: { "Content-Type": "application/json" }, method: "POST", }); const res = await createModelViaHandler(req, db); expect(res.status).toBe(201); const body = (await res.json()) as { model: Model }; expect(body.model.name).toBe("GPT-4o"); expect(body.model.modelId).toBe("gpt-4o"); }); }); test("GET /api/models list", async () => { await withRouteDb(async (db) => { const p1 = seedProvider(db, "ListP1"); const p2 = seedProvider(db, "ListP2"); createTestModel(db, "A-Model", p1); createTestModel(db, "B-Model", p2); const req = new Request("http://localhost/api/models?page=1&pageSize=20"); const res = await listModelsViaHandler(req, db); expect(res.status).toBe(200); const body = (await res.json()) as { items: Model[]; total: number }; expect(body.total).toBe(2); expect(body.items.length).toBe(2); }); }); test("GET /api/models filter by providerId", async () => { await withRouteDb(async (db) => { const p1 = seedProvider(db, "P1"); const p2 = seedProvider(db, "P2"); createTestModel(db, "M1", p1); createTestModel(db, "M2", p2); const res = await listModelsViaHandler( new Request("http://localhost/api/models?page=1&pageSize=20&providerId=" + p1), db, ); expect(res.status).toBe(200); const body = (await res.json()) as { items: Model[]; total: number }; expect(body.total).toBe(1); }); }); test("GET /api/models/:id get detail", async () => { await withRouteDb(async (db) => { const model = createTestModel(db, "Detail"); const req = new Request("http://localhost/api/models/" + model.id); const res = await getModelViaHandler(req, db); expect(res.status).toBe(200); const body = (await res.json()) as { model: Model }; expect(body.model.name).toBe("Detail"); }); }); test("PATCH /api/models/:id update", async () => { await withRouteDb(async (db) => { const model = createTestModel(db, "OldName"); const req = new Request("http://localhost/api/models/" + model.id, { body: JSON.stringify({ name: "Updated" }), headers: { "Content-Type": "application/json" }, method: "PATCH", }); const res = await updateModelViaHandler(req, db); expect(res.status).toBe(200); const body = (await res.json()) as { model: Model }; expect(body.model.name).toBe("Updated"); }); }); test("POST /api/models/:id/enable", async () => { await withRouteDb(async (db) => { const model = createTestModel(db, "EnableTest"); await disableModelViaHandler( new Request("http://localhost/api/models/" + model.id + "/disable", { method: "POST" }), db, ); const req = new Request("http://localhost/api/models/" + model.id + "/enable", { method: "POST" }); const res = await enableModelViaHandler(req, db); expect(res.status).toBe(200); const body = (await res.json()) as { model: Model }; expect(body.model.enabled).toBe(true); }); }); test("POST /api/models/:id/disable", async () => { await withRouteDb(async (db) => { const model = createTestModel(db, "DisableTest"); const req = new Request("http://localhost/api/models/" + model.id + "/disable", { method: "POST" }); const res = await disableModelViaHandler(req, db); expect(res.status).toBe(200); const body = (await res.json()) as { model: Model }; expect(body.model.enabled).toBe(false); }); }); test("DELETE /api/models/:id", async () => { await withRouteDb(async (db) => { const model = createTestModel(db, "DeleteTest"); const req = new Request("http://localhost/api/models/" + model.id, { method: "DELETE" }); const res = await deleteModelViaHandler(req, db); expect(res.status).toBe(204); }); }); test("invalid capabilities returns 400", async () => { await withRouteDb(async (db) => { const providerId = seedProvider(db); const req = new Request("http://localhost/api/models", { body: JSON.stringify({ capabilities: ["invalid-cap"], modelId: "test", name: "Test", providerId, }), headers: { "Content-Type": "application/json" }, method: "POST", }); const res = await createModelViaHandler(req, db); expect(res.status).toBe(400); }); }); });