test(documents): update vault AI route mocks for document provider order

Point route + backfill test mocks at the document-scoped resolvers and the
document-egress consent gate.

Co-Authored-By: Marc-André Bombeck
This commit is contained in:
Marc-André BombeckandMarc-André Bombeck committed 2026-07-08 23:51:50 +02:00
1 parent 8e5f5f04ae
commit c1e11e284a
5 files changed
+41 -37

No files matched your search

@@ -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);
@@ -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 () => {
@@ -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: {} },
@@ -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);
@@ -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" },