Files
2026-07-13 13:39:12 +08:00

189 lines
7.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const { POST, OPTIONS } = await import("../../src/app/api/v1/ocr/route.ts");
const { handleOcr } = await import("../../open-sse/handlers/ocr.ts");
const { OCR_PROVIDERS, getOcrProvider, parseOcrModel, getAllOcrModels } =
await import("../../open-sse/config/ocrRegistry.ts");
const { v1OcrSchema } = await import("../../src/shared/validation/schemas/apiV1.ts");
const originalFetch = globalThis.fetch;
test.afterEach(() => {
globalThis.fetch = originalFetch;
});
function ocrRequest(body: string) {
return new Request("http://localhost/v1/ocr", {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});
}
// ── Registry ───────────────────────────────────────────────────────────────
test("OCR_PROVIDERS registers mistral with the Mistral OCR base URL", () => {
assert.equal(OCR_PROVIDERS.mistral.baseUrl, "https://api.mistral.ai/v1/ocr");
const provider = getOcrProvider("mistral");
assert.ok(provider);
assert.equal(provider.authHeader, "bearer");
assert.ok(provider.models.some((m: { id: string }) => m.id === "mistral-ocr-latest"));
});
test("parseOcrModel routes bare and prefixed mistral models to the mistral provider", () => {
assert.deepEqual(parseOcrModel("mistral-ocr-latest"), {
provider: "mistral",
model: "mistral-ocr-latest",
});
assert.deepEqual(parseOcrModel("mistral/mistral-ocr-latest"), {
provider: "mistral",
model: "mistral-ocr-latest",
});
// Unknown model → no provider resolved
assert.deepEqual(parseOcrModel("mystery-model"), {
provider: null,
model: "mystery-model",
});
});
test("getAllOcrModels exposes the mistral OCR model with a provider prefix", () => {
const models = getAllOcrModels();
assert.ok(models.some((m: { id: string }) => m.id === "mistral/mistral-ocr-latest"));
});
// ── Schema (Zod, Rule #7) ────────────────────────────────────────────────────
test("v1OcrSchema rejects a body without a document", () => {
const result = v1OcrSchema.safeParse({ model: "mistral-ocr-latest" });
assert.equal(result.success, false);
});
test("v1OcrSchema accepts a document_url document object", () => {
const result = v1OcrSchema.safeParse({
model: "mistral-ocr-latest",
document: { type: "document_url", document_url: "https://example.com/a.pdf" },
});
assert.equal(result.success, true);
});
test("v1OcrSchema accepts an image_url document object", () => {
const result = v1OcrSchema.safeParse({
document: { type: "image_url", image_url: "https://example.com/a.png" },
});
assert.equal(result.success, true);
});
// ── Route (public /v1/ocr entry point) ───────────────────────────────────────
test("POST /v1/ocr returns 400 for invalid JSON without leaking a stack trace", async () => {
const response = await POST(ocrRequest("not json at all"));
const body = (await response.json()) as any;
assert.equal(response.status, 400);
assert.equal(body.error.message, "Invalid JSON body");
// Rule #12 — error responses must never leak stack traces.
assert.ok(!body.error.message.includes("at /"));
});
test("OPTIONS /v1/ocr answers the CORS preflight", async () => {
const response = await OPTIONS();
assert.equal(response.status, 200);
assert.match(response.headers.get("access-control-allow-methods") || "", /OPTIONS/);
});
// ── Handler ──────────────────────────────────────────────────────────────────
test("handleOcr requires a document", async () => {
const response = await handleOcr({
body: { model: "mistral-ocr-latest" },
credentials: { apiKey: "sk-test" },
});
const payload = (await response.json()) as any;
assert.equal(response.status, 400);
assert.equal(payload.error.message, "document is required");
});
test("handleOcr rejects unknown OCR models", async () => {
const response = await handleOcr({
body: { model: "mystery/ocr", document: { document_url: "x" } },
credentials: { apiKey: "sk-test" },
});
const payload = (await response.json()) as any;
assert.equal(response.status, 400);
assert.match(payload.error.message, /No OCR provider found/);
});
test("handleOcr requires credentials for the resolved provider", async () => {
const response = await handleOcr({
body: { document: { document_url: "x" } },
credentials: null,
});
const payload = (await response.json()) as any;
assert.equal(response.status, 401);
assert.equal(payload.error.message, "No credentials for OCR provider: mistral");
});
test("handleOcr proxies a successful request to the mistral OCR endpoint", async () => {
let captured: any;
globalThis.fetch = async (url: any, options: any = {}) => {
captured = {
url: String(url),
headers: options.headers,
body: JSON.parse(String(options.body || "{}")),
};
return Response.json({ pages: [{ index: 0, markdown: "hello" }] });
};
const response = await handleOcr({
body: { document: { type: "document_url", document_url: "https://example.com/a.pdf" } },
credentials: { apiKey: "sk-mistral" },
});
assert.equal(captured.url, "https://api.mistral.ai/v1/ocr");
assert.equal(captured.headers.Authorization, "Bearer sk-mistral");
// model defaults to mistral-ocr-latest and the document is forwarded upstream.
assert.equal(captured.body.model, "mistral-ocr-latest");
assert.deepEqual(captured.body.document, {
type: "document_url",
document_url: "https://example.com/a.pdf",
});
assert.equal(response.status, 200);
assert.deepEqual(await response.json(), { pages: [{ index: 0, markdown: "hello" }] });
});
test("handleOcr passes upstream error payloads through with the upstream status", async () => {
globalThis.fetch = async () =>
new Response('{"error":"bad request"}', {
status: 422,
headers: { "content-type": "application/json" },
});
const response = await handleOcr({
body: { model: "mistral/mistral-ocr-latest", document: { document_url: "x" } },
credentials: { apiKey: "sk-test" },
});
assert.equal(response.status, 422);
assert.equal(await response.text(), '{"error":"bad request"}');
});
test("handleOcr returns a sanitized 500 when the upstream request throws", async () => {
globalThis.fetch = async () => {
throw new Error("socket closed");
};
const response = await handleOcr({
body: { model: "mistral-ocr-latest", document: { document_url: "x" } },
credentials: { apiKey: "sk-test" },
});
const payload = (await response.json()) as any;
assert.equal(response.status, 500);
assert.match(payload.error.message, /OCR request failed: socket closed/);
assert.ok(!payload.error.message.includes("at /"));
});