import test from "node:test"; import assert from "node:assert/strict"; const BASE_URL = "http://localhost:20128"; test("contract: /api/v1 OPTIONS exposes CORS and allowed methods", async () => { const { OPTIONS } = await import("../../src/app/api/v1/route.ts"); const response = await OPTIONS(); assert.equal(response.status, 200); assert.ok(response.headers.has("Access-Control-Allow-Methods")); }); test("contract: /api/v1/embeddings OPTIONS exposes POST/GET/OPTIONS", async () => { const { OPTIONS } = await import("../../src/app/api/v1/embeddings/route.ts"); const response = await OPTIONS(); const allowMethods = response.headers.get("Access-Control-Allow-Methods") || ""; assert.equal(response.status, 200); assert.ok(allowMethods.includes("GET")); assert.ok(allowMethods.includes("POST")); assert.ok(allowMethods.includes("OPTIONS")); }); test("contract: /api/v1 and /api/v1/models return consistent model IDs", async () => { const [{ GET: getV1 }, { GET: getV1Models }] = await Promise.all([ import("../../src/app/api/v1/route.ts"), import("../../src/app/api/v1/models/route.ts"), ]); const [v1Response, v1ModelsResponse] = await Promise.all([ getV1(new Request(`${BASE_URL}/api/v1`, { method: "GET" })), getV1Models(new Request(`${BASE_URL}/api/v1/models`, { method: "GET" })), ]); assert.equal(v1Response.status, 200); assert.equal(v1ModelsResponse.status, 200); const v1Body = (await v1Response.json()) as any; const v1ModelsBody = (await v1ModelsResponse.json()) as any; assert.equal(v1Body.object, "list"); assert.equal(v1ModelsBody.object, "list"); assert.ok(Array.isArray(v1Body.data)); assert.ok(Array.isArray(v1ModelsBody.data)); const v1Ids = [...new Set(v1Body.data.map((item: any) => item.id))].sort(); const v1ModelsIds = [...new Set(v1ModelsBody.data.map((item: any) => item.id))].sort(); assert.deepEqual(v1Ids, v1ModelsIds); }); test("contract: /api/v1/models returns OpenAI-compatible model shape", async () => { const { GET: getV1Models } = await import("../../src/app/api/v1/models/route.ts"); const response = await getV1Models(new Request(`${BASE_URL}/api/v1/models`, { method: "GET" })); assert.equal(response.status, 200); const body = (await response.json()) as any; assert.equal(body.object, "list"); assert.ok(Array.isArray(body.data)); // In CI environments without provider connections, models list may be empty — skip shape check if (body.data.length > 0) { const first = body.data[0]; assert.equal(typeof first.id, "string"); assert.equal(first.object, "model"); assert.equal(typeof first.created, "number"); assert.equal(typeof first.owned_by, "string"); } }); test("contract: /api/v1/embeddings GET returns embedding model listing shape", async () => { const { GET: getEmbeddings } = await import("../../src/app/api/v1/embeddings/route.ts"); const response = await getEmbeddings(); assert.equal(response.status, 200); const body = (await response.json()) as any; assert.equal(body.object, "list"); assert.ok(Array.isArray(body.data)); // In CI environments without provider connections, the filtered specialty catalog may be empty. if (body.data.length > 0) { const first = body.data[0]; assert.equal(first.object, "model"); assert.equal(first.type, "embedding"); assert.equal(typeof first.id, "string"); assert.equal(typeof first.owned_by, "string"); } }); test("contract: /api/v1/images/generations GET returns image model listing shape", async () => { const { GET: getImageModels } = await import("../../src/app/api/v1/images/generations/route.ts"); const response = await getImageModels(); assert.equal(response.status, 200); const body = (await response.json()) as any; assert.equal(body.object, "list"); assert.ok(Array.isArray(body.data)); // In CI environments without provider connections, the filtered specialty catalog may be empty. if (body.data.length > 0) { const first = body.data[0]; assert.equal(first.object, "model"); assert.equal(first.type, "image"); assert.equal(typeof first.id, "string"); assert.equal(typeof first.owned_by, "string"); } }); test("contract: /api/v1/messages/count_tokens returns 400 on invalid JSON", async () => { const { POST: countTokens } = await import("../../src/app/api/v1/messages/count_tokens/route.ts"); const response = await countTokens( new Request(`${BASE_URL}/api/v1/messages/count_tokens`, { method: "POST", headers: { "Content-Type": "application/json" }, body: "not-json", }) ); assert.equal(response.status, 400); const body = (await response.json()) as any; assert.ok(body.error, "error payload should exist"); assert.ok( typeof body.error === "string" || typeof body.error === "object", "error payload should be string or object" ); }); test("contract: /api/v1/messages/count_tokens rejects empty messages payload", async () => { const { POST: countTokens } = await import("../../src/app/api/v1/messages/count_tokens/route.ts"); const response = await countTokens( new Request(`${BASE_URL}/api/v1/messages/count_tokens`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages: [] }), }) ); assert.equal(response.status, 400); const body = (await response.json()) as any; assert.ok(body.error, "error payload should exist"); assert.ok( typeof body.error === "string" || typeof body.error === "object", "error payload should be string or object" ); }); test("contract: /api/v1/messages/count_tokens computes token estimate from text content", async () => { const { POST: countTokens } = await import("../../src/app/api/v1/messages/count_tokens/route.ts"); const payload = { messages: [ { role: "user", content: "abcd" }, // 4 chars { role: "assistant", content: [{ type: "text", text: "12345678" }], // 8 chars }, ], }; const response = await countTokens( new Request(`${BASE_URL}/api/v1/messages/count_tokens`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }) ); assert.equal(response.status, 200); const body = (await response.json()) as any; // Real tiktoken count (countTextTokens): "abcd" => 1, "12345678" => 3 (digits split). // The previous expectation (3) was the old ceil(chars/4) heuristic, replaced by the // tiktoken-based estimator; the accurate total for this payload is 4. assert.equal(body.input_tokens, 4); });