diff --git a/src/app/api/documents/inbound/[id]/extract/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/extract/__tests__/route.test.ts index 1dde37d5d..b3a264742 100644 --- a/src/app/api/documents/inbound/[id]/extract/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/extract/__tests__/route.test.ts @@ -28,12 +28,13 @@ vi.mock("@/lib/ai/coach/bytes-codec", () => ({ decryptFromBytes: vi.fn(() => "{}"), })); -vi.mock("@/lib/labs/ocr-capability", () => ({ - resolveVisionProvider: vi.fn(), - resolveTextProvider: vi.fn(), +vi.mock("@/lib/documents/provider-order", () => ({ + resolveDocumentVisionProvider: vi.fn(), + resolveDocumentTextProvider: vi.fn(), })); vi.mock("@/lib/ai/consent-guard", () => ({ - assertConsentForChain: vi.fn().mockResolvedValue(undefined), + assertDocumentEgressConsent: vi.fn().mockResolvedValue(undefined), + ConsentRequiredError: class ConsentRequiredError extends Error {}, })); vi.mock("@/lib/ai/coach/budget", () => ({ buildDateKey: vi.fn(() => "2026-06-27"), @@ -76,7 +77,7 @@ import { POST } from "../route"; import { prisma } from "@/lib/db"; import { getSession } from "@/lib/auth/session"; import { requireModuleEnabled } from "@/lib/modules/gate"; -import { resolveVisionProvider } from "@/lib/labs/ocr-capability"; +import { resolveDocumentVisionProvider } from "@/lib/documents/provider-order"; const SESSION_OK = { session: { id: "sess-1", expiresAt: new Date(Date.now() + 3_600_000) }, @@ -109,7 +110,7 @@ beforeEach(() => { describe("POST /api/documents/inbound/[id]/extract", () => { it("422s without a vision provider and leaves the stored row intact", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue({ + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue({ chain: [], pick: null, } as never); diff --git a/src/app/api/documents/inbound/[id]/index/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/index/__tests__/route.test.ts index 75246fefe..c132d5d6b 100644 --- a/src/app/api/documents/inbound/[id]/index/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/index/__tests__/route.test.ts @@ -29,11 +29,12 @@ vi.mock("@/lib/documents/describe", () => ({ vi.mock("@/lib/documents/content-index", () => ({ upsertContentIndex: vi.fn().mockResolvedValue({ tokenCount: 7 }), })); -vi.mock("@/lib/labs/ocr-capability", () => ({ - resolveVisionProvider: vi.fn(), +vi.mock("@/lib/documents/provider-order", () => ({ + resolveDocumentVisionProvider: vi.fn(), })); vi.mock("@/lib/ai/consent-guard", () => ({ - assertConsentForChain: vi.fn().mockResolvedValue(undefined), + assertDocumentEgressConsent: vi.fn().mockResolvedValue(undefined), + ConsentRequiredError: class ConsentRequiredError extends Error {}, })); vi.mock("@/lib/ai/coach/budget", () => ({ buildDateKey: vi.fn(() => "2026-07-07"), @@ -68,7 +69,7 @@ vi.mock("next/headers", () => ({ import { POST } from "../route"; import { prisma } from "@/lib/db"; import { getSession } from "@/lib/auth/session"; -import { resolveVisionProvider } from "@/lib/labs/ocr-capability"; +import { resolveDocumentVisionProvider } from "@/lib/documents/provider-order"; import { transcribeDocument } from "@/lib/documents/describe"; import { upsertContentIndex } from "@/lib/documents/content-index"; @@ -109,7 +110,7 @@ beforeEach(() => { describe("POST /api/documents/inbound/[id]/index", () => { it("vision: transcribes then upserts the index, never facts", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue({ + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue({ chain: [{ providerType: "anthropic", instance: {} }], pick: { entry: { providerType: "anthropic", instance: {} }, @@ -137,7 +138,7 @@ describe("POST /api/documents/inbound/[id]/index", () => { }); it("vision: 422 without a provider", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue({ + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue({ chain: [], pick: null, } as never); @@ -160,7 +161,7 @@ describe("POST /api/documents/inbound/[id]/index", () => { expect.objectContaining({ source: "text-ocr", providerType: null }), ); // No provider was resolved on the text path. - expect(resolveVisionProvider).not.toHaveBeenCalled(); + expect(resolveDocumentVisionProvider).not.toHaveBeenCalled(); }); it("text: 422 when local OCR is not enabled", async () => { diff --git a/src/app/api/documents/inbound/[id]/suggest/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/suggest/__tests__/route.test.ts index 8c506d40b..3db7bbd6a 100644 --- a/src/app/api/documents/inbound/[id]/suggest/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/suggest/__tests__/route.test.ts @@ -27,12 +27,13 @@ vi.mock("@/lib/documents/assist", () => ({ runDocumentAssist: vi.fn(), DocumentAssistError: class DocumentAssistError extends Error {}, })); -vi.mock("@/lib/labs/ocr-capability", () => ({ - resolveVisionProvider: vi.fn(), - resolveTextProvider: vi.fn(), +vi.mock("@/lib/documents/provider-order", () => ({ + resolveDocumentVisionProvider: vi.fn(), + resolveDocumentTextProvider: vi.fn(), })); vi.mock("@/lib/ai/consent-guard", () => ({ - assertConsentForChain: vi.fn().mockResolvedValue(undefined), + assertDocumentEgressConsent: vi.fn().mockResolvedValue(undefined), + ConsentRequiredError: class ConsentRequiredError extends Error {}, })); vi.mock("@/lib/ai/coach/budget", () => ({ buildDateKey: vi.fn(() => "2026-07-07"), @@ -67,7 +68,7 @@ vi.mock("next/headers", () => ({ import { POST } from "../route"; import { prisma } from "@/lib/db"; import { getSession } from "@/lib/auth/session"; -import { resolveVisionProvider } from "@/lib/labs/ocr-capability"; +import { resolveDocumentVisionProvider } from "@/lib/documents/provider-order"; import { runDocumentAssist } from "@/lib/documents/assist"; const SESSION_OK = { @@ -98,7 +99,7 @@ beforeEach(() => { describe("POST /api/documents/inbound/[id]/suggest", () => { it("422s without a vision provider and writes nothing", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue({ + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue({ chain: [], pick: null, } as never); @@ -113,7 +114,7 @@ describe("POST /api/documents/inbound/[id]/suggest", () => { }); it("returns drafts and never persists them", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue({ + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue({ chain: [{ providerType: "anthropic", instance: {} }], pick: { entry: { providerType: "anthropic", instance: {} }, diff --git a/src/app/api/documents/inbound/[id]/summary/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/summary/__tests__/route.test.ts index ee1b05c9f..80607b9f1 100644 --- a/src/app/api/documents/inbound/[id]/summary/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/summary/__tests__/route.test.ts @@ -27,12 +27,13 @@ vi.mock("@/lib/documents/describe", () => ({ transcribeDocument: vi.fn(), DocumentDescribeError: class DocumentDescribeError extends Error {}, })); -vi.mock("@/lib/labs/ocr-capability", () => ({ - resolveVisionProvider: vi.fn(), - resolveTextProvider: vi.fn(), +vi.mock("@/lib/documents/provider-order", () => ({ + resolveDocumentVisionProvider: vi.fn(), + resolveDocumentTextProvider: vi.fn(), })); vi.mock("@/lib/ai/consent-guard", () => ({ - assertConsentForChain: vi.fn().mockResolvedValue(undefined), + assertDocumentEgressConsent: vi.fn().mockResolvedValue(undefined), + ConsentRequiredError: class ConsentRequiredError extends Error {}, })); vi.mock("@/lib/ai/coach/budget", () => ({ buildDateKey: vi.fn(() => "2026-07-07"), @@ -67,7 +68,7 @@ vi.mock("next/headers", () => ({ import { POST } from "../route"; import { prisma } from "@/lib/db"; import { getSession } from "@/lib/auth/session"; -import { resolveVisionProvider } from "@/lib/labs/ocr-capability"; +import { resolveDocumentVisionProvider } from "@/lib/documents/provider-order"; import { auditLog } from "@/lib/auth/audit"; import { runDocumentSummary, @@ -105,7 +106,7 @@ beforeEach(() => { mimeType: "image/png", status: "STORED", } as never); - vi.mocked(resolveVisionProvider).mockResolvedValue({ + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue({ chain: [{ providerType: "anthropic", instance: {} }], pick: { entry: { providerType: "anthropic", instance: {} }, @@ -151,7 +152,7 @@ describe("POST /api/documents/inbound/[id]/summary", () => { }); it("422s without a provider", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue({ + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue({ chain: [], pick: null, } as never); diff --git a/src/lib/jobs/__tests__/document-content-index-backfill.test.ts b/src/lib/jobs/__tests__/document-content-index-backfill.test.ts index e84a69e1b..dff362c26 100644 --- a/src/lib/jobs/__tests__/document-content-index-backfill.test.ts +++ b/src/lib/jobs/__tests__/document-content-index-backfill.test.ts @@ -25,11 +25,11 @@ vi.mock("@/lib/documents/describe", () => ({ vi.mock("@/lib/documents/content-index", () => ({ upsertContentIndex: vi.fn().mockResolvedValue({ tokenCount: 3 }), })); -vi.mock("@/lib/labs/ocr-capability", () => ({ - resolveVisionProvider: vi.fn(), +vi.mock("@/lib/documents/provider-order", () => ({ + resolveDocumentVisionProvider: vi.fn(), })); vi.mock("@/lib/ai/consent-guard", () => ({ - assertConsentForChain: vi.fn().mockResolvedValue(undefined), + assertDocumentEgressConsent: vi.fn().mockResolvedValue(undefined), ConsentRequiredError: class ConsentRequiredError extends Error {}, })); vi.mock("@/lib/ai/coach/budget", () => ({ @@ -42,9 +42,9 @@ vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn() })); import { runContentIndexBackfillForUser } from "../document-content-index-backfill"; import { prisma } from "@/lib/db"; -import { resolveVisionProvider } from "@/lib/labs/ocr-capability"; +import { resolveDocumentVisionProvider } from "@/lib/documents/provider-order"; import { - assertConsentForChain, + assertDocumentEgressConsent, ConsentRequiredError, } from "@/lib/ai/consent-guard"; import { reserveBudget } from "@/lib/ai/coach/budget"; @@ -81,7 +81,7 @@ beforeEach(() => { describe("runContentIndexBackfillForUser", () => { it("no-ops with no provider", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue({ + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue({ chain: [], pick: null, } as never); @@ -91,8 +91,8 @@ describe("runContentIndexBackfillForUser", () => { }); it("no-ops when consent is missing", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue(PICK as never); - vi.mocked(assertConsentForChain).mockRejectedValueOnce( + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue(PICK as never); + vi.mocked(assertDocumentEgressConsent).mockRejectedValueOnce( new ConsentRequiredError("insights" as never), ); const result = await runContentIndexBackfillForUser("user-1"); @@ -101,7 +101,7 @@ describe("runContentIndexBackfillForUser", () => { }); it("indexes the not-yet-indexed documents", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue(PICK as never); + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue(PICK as never); // One short page (< PAGE_SIZE) — the walk breaks after it, so a single // findMany return is enough (a trailing once would leak to the next test). vi.mocked(prisma.inboundDocument.findMany).mockResolvedValue([ @@ -115,7 +115,7 @@ describe("runContentIndexBackfillForUser", () => { }); it("stops when the daily budget is reached (resumable)", async () => { - vi.mocked(resolveVisionProvider).mockResolvedValue(PICK as never); + vi.mocked(resolveDocumentVisionProvider).mockResolvedValue(PICK as never); vi.mocked(prisma.inboundDocument.findMany).mockResolvedValueOnce([ { id: "d1" }, { id: "d2" },