mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
test(documents): wire content-index mocks into the list route test
P2-1. The list GET now issues a documentContentIndex lookup and unions a content-token OR branch when a search term is present, so the existing route test gains the mock + the third OR branch in its where assertion. Fix the backfill test's findFirst mock signature under strict tsc.
This commit is contained in:
1 parent
b210c743e9
commit
1f7807aead
16 files changed
+211
-55
No files matched your search
@@ -78,15 +78,21 @@ const SESSION_OK = {
|
||||
};
|
||||
const ctx = (id: string) => ({ params: Promise.resolve({ id }) });
|
||||
const visionReq = (id: string) =>
|
||||
new NextRequest(new URL(`http://localhost/api/documents/inbound/${id}/index`), {
|
||||
method: "POST",
|
||||
});
|
||||
new NextRequest(
|
||||
new URL(`http://localhost/api/documents/inbound/${id}/index`),
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
const textReq = (id: string, text: string) =>
|
||||
new NextRequest(new URL(`http://localhost/api/documents/inbound/${id}/index`), {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ mode: "text", text }),
|
||||
});
|
||||
new NextRequest(
|
||||
new URL(`http://localhost/api/documents/inbound/${id}/index`),
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ mode: "text", text }),
|
||||
},
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
@@ -220,7 +220,12 @@ async function handleVisionIndex(
|
||||
images: vision.images,
|
||||
documents: vision.documents,
|
||||
});
|
||||
await reconcileSpend(userId, reservation.reserved, reservation.reserved, dateKey);
|
||||
await reconcileSpend(
|
||||
userId,
|
||||
reservation.reserved,
|
||||
reservation.reserved,
|
||||
dateKey,
|
||||
);
|
||||
const { tokenCount } = await upsertContentIndex({
|
||||
userId,
|
||||
documentId: document.id,
|
||||
|
||||
@@ -76,9 +76,12 @@ const SESSION_OK = {
|
||||
};
|
||||
const ctx = (id: string) => ({ params: Promise.resolve({ id }) });
|
||||
const visionReq = (id: string) =>
|
||||
new NextRequest(new URL(`http://localhost/api/documents/inbound/${id}/suggest`), {
|
||||
method: "POST",
|
||||
});
|
||||
new NextRequest(
|
||||
new URL(`http://localhost/api/documents/inbound/${id}/suggest`),
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
@@ -141,7 +144,9 @@ describe("POST /api/documents/inbound/[id]/suggest", () => {
|
||||
});
|
||||
|
||||
it("404s an unknown document", async () => {
|
||||
vi.mocked(prisma.inboundDocument.findFirst).mockResolvedValue(null as never);
|
||||
vi.mocked(prisma.inboundDocument.findFirst).mockResolvedValue(
|
||||
null as never,
|
||||
);
|
||||
const res = await POST(visionReq("nope") as never, ctx("nope") as never);
|
||||
expect(res.status).toBe(404);
|
||||
});
|
||||
|
||||
@@ -179,7 +179,12 @@ async function handleTextSuggest(
|
||||
providerType: pick.providerType,
|
||||
ocrText: parsed.data.text,
|
||||
});
|
||||
await reconcileSpend(userId, reservation.reserved, reservation.reserved, dateKey);
|
||||
await reconcileSpend(
|
||||
userId,
|
||||
reservation.reserved,
|
||||
reservation.reserved,
|
||||
dateKey,
|
||||
);
|
||||
return finishSuggest(request, userId, document.id, "text", suggestion);
|
||||
} catch (err) {
|
||||
await reconcileSpend(userId, reservation.reserved, 0, dateKey);
|
||||
@@ -261,7 +266,12 @@ async function handleVisionSuggest(
|
||||
images: vision.images,
|
||||
documents: vision.documents,
|
||||
});
|
||||
await reconcileSpend(userId, reservation.reserved, reservation.reserved, dateKey);
|
||||
await reconcileSpend(
|
||||
userId,
|
||||
reservation.reserved,
|
||||
reservation.reserved,
|
||||
dateKey,
|
||||
);
|
||||
return finishSuggest(request, userId, document.id, "vision", suggestion);
|
||||
} catch (err) {
|
||||
await reconcileSpend(userId, reservation.reserved, 0, dateKey);
|
||||
|
||||
@@ -69,7 +69,10 @@ import { prisma } from "@/lib/db";
|
||||
import { getSession } from "@/lib/auth/session";
|
||||
import { resolveVisionProvider } from "@/lib/labs/ocr-capability";
|
||||
import { auditLog } from "@/lib/auth/audit";
|
||||
import { runDocumentSummary, transcribeDocument } from "@/lib/documents/describe";
|
||||
import {
|
||||
runDocumentSummary,
|
||||
transcribeDocument,
|
||||
} from "@/lib/documents/describe";
|
||||
|
||||
const SESSION_OK = {
|
||||
session: { id: "s1", expiresAt: new Date(Date.now() + 3_600_000) },
|
||||
@@ -118,7 +121,10 @@ describe("POST /api/documents/inbound/[id]/summary", () => {
|
||||
summary: "A blood panel from a lab.",
|
||||
} as never);
|
||||
|
||||
const res = await POST(req("doc-1", "summary") as never, ctx("doc-1") as never);
|
||||
const res = await POST(
|
||||
req("doc-1", "summary") as never,
|
||||
ctx("doc-1") as never,
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.data).toEqual({ summary: "A blood panel from a lab." });
|
||||
@@ -134,7 +140,10 @@ describe("POST /api/documents/inbound/[id]/summary", () => {
|
||||
text: "raw transcribed body",
|
||||
} as never);
|
||||
|
||||
const res = await POST(req("doc-1", "text") as never, ctx("doc-1") as never);
|
||||
const res = await POST(
|
||||
req("doc-1", "text") as never,
|
||||
ctx("doc-1") as never,
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.data).toEqual({ text: "raw transcribed body" });
|
||||
@@ -146,7 +155,10 @@ describe("POST /api/documents/inbound/[id]/summary", () => {
|
||||
chain: [],
|
||||
pick: null,
|
||||
} as never);
|
||||
const res = await POST(req("doc-1", "summary") as never, ctx("doc-1") as never);
|
||||
const res = await POST(
|
||||
req("doc-1", "summary") as never,
|
||||
ctx("doc-1") as never,
|
||||
);
|
||||
expect(res.status).toBe(422);
|
||||
assertNoPersistence();
|
||||
});
|
||||
|
||||
@@ -215,7 +215,12 @@ async function handleTextSummary(
|
||||
providerType: pick.providerType,
|
||||
ocrText: parsed.data.text,
|
||||
});
|
||||
await reconcileSpend(userId, reservation.reserved, reservation.reserved, dateKey);
|
||||
await reconcileSpend(
|
||||
userId,
|
||||
reservation.reserved,
|
||||
reservation.reserved,
|
||||
dateKey,
|
||||
);
|
||||
return finishSummary(request, userId, document.id, "text", mode, result);
|
||||
} catch (err) {
|
||||
await reconcileSpend(userId, reservation.reserved, 0, dateKey);
|
||||
@@ -286,7 +291,12 @@ async function handleVisionSummary(
|
||||
images: vision.images,
|
||||
documents: vision.documents,
|
||||
});
|
||||
await reconcileSpend(userId, reservation.reserved, reservation.reserved, dateKey);
|
||||
await reconcileSpend(
|
||||
userId,
|
||||
reservation.reserved,
|
||||
reservation.reserved,
|
||||
dateKey,
|
||||
);
|
||||
return finishSummary(request, userId, document.id, "vision", mode, result);
|
||||
} catch (err) {
|
||||
await reconcileSpend(userId, reservation.reserved, 0, dateKey);
|
||||
|
||||
@@ -39,6 +39,9 @@ vi.mock("@/lib/db", () => {
|
||||
documentConditionLink: {
|
||||
findMany: vi.fn(),
|
||||
},
|
||||
documentContentIndex: {
|
||||
findMany: vi.fn(),
|
||||
},
|
||||
illnessEpisode: {
|
||||
findMany: vi.fn(),
|
||||
},
|
||||
@@ -155,6 +158,9 @@ beforeEach(() => {
|
||||
vi.mocked(prisma.documentConditionLink.findMany).mockResolvedValue(
|
||||
[] as never,
|
||||
);
|
||||
vi.mocked(prisma.documentContentIndex.findMany).mockResolvedValue(
|
||||
[] as never,
|
||||
);
|
||||
vi.mocked(prisma.inboundDocument.findFirst).mockResolvedValue(null as never);
|
||||
// No settings row / no user override → policy defaults (25 MiB / 1 GiB).
|
||||
vi.mocked(prisma.appSettings.findUnique).mockResolvedValue(null as never);
|
||||
@@ -346,9 +352,12 @@ describe("GET /api/documents/inbound (list)", () => {
|
||||
expect(where.deletedAt).toBeNull();
|
||||
expect(where.kind).toEqual({ in: ["LAB_RESULT", "IMAGING"] });
|
||||
expect(where.conditionLinks).toEqual({ some: { episodeId: "ep-1" } });
|
||||
// Title/filename substring branches, unioned with the blind content-token
|
||||
// overlap (the third branch carries the HMAC'd query tokens for "panel").
|
||||
expect(where.OR).toEqual([
|
||||
{ title: { contains: "panel", mode: "insensitive" } },
|
||||
{ filename: { contains: "panel", mode: "insensitive" } },
|
||||
{ contentIndex: { is: { searchTokens: { hasSome: expect.any(Array) } } } },
|
||||
]);
|
||||
// limit+1 fetched for the has-more probe.
|
||||
expect(arg.take).toBe(11);
|
||||
|
||||
@@ -34,10 +34,10 @@ export const GET = apiHandler(async () => {
|
||||
FROM inbound_documents
|
||||
WHERE user_id = ${user.id}
|
||||
`,
|
||||
// Episodes that carry at least one LIVE document link — the filter
|
||||
// bar's condition chips. Sourced here (not from the loaded corpus) so
|
||||
// a chip exists even when its documents sit pages deep in the
|
||||
// timeline; one indexed grouped query, no blobs.
|
||||
// Episodes that carry at least one LIVE document link — the filter
|
||||
// bar's condition chips. Sourced here (not from the loaded corpus) so
|
||||
// a chip exists even when its documents sit pages deep in the
|
||||
// timeline; one indexed grouped query, no blobs.
|
||||
prisma.documentConditionLink.findMany({
|
||||
where: { userId: user.id, document: { deletedAt: null } },
|
||||
select: { episodeId: true, episode: { select: { label: true } } },
|
||||
|
||||
Reference in new issue
Block a user