Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
76 lines
3.1 KiB
TypeScript
76 lines
3.1 KiB
TypeScript
import type { Actor } from "@rakazo/contracts";
|
|
import type { PrismaClient } from "@rakazo/db";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { querySpaceSearch } from "./search.js";
|
|
|
|
const actor: Actor = { spaceId: "space-test", userId: "user-test" };
|
|
|
|
function fixture(count: number) {
|
|
const artifacts = Array.from({ length: count }, (_, index) => ({
|
|
id: `artifact-${index}`,
|
|
botId: `bot-${index}`,
|
|
bot: { name: "Bot" },
|
|
name: "report.txt",
|
|
mimeType: "text/plain",
|
|
size: 10,
|
|
}));
|
|
const groupArtifacts = artifacts.map((artifact) => ({
|
|
...artifact,
|
|
id: `group-${artifact.id}`,
|
|
botId: null,
|
|
bot: null,
|
|
groupId: "group-test",
|
|
group: { name: "Group" },
|
|
}));
|
|
const prisma = {
|
|
bot: { findMany: vi.fn().mockResolvedValue([]) },
|
|
chatGroup: { findMany: vi.fn().mockResolvedValue([]) },
|
|
artifact: {
|
|
findMany: vi.fn().mockResolvedValueOnce(artifacts).mockResolvedValueOnce(groupArtifacts),
|
|
},
|
|
routine: { findMany: vi.fn().mockResolvedValue([]) },
|
|
$queryRaw: vi.fn().mockResolvedValue([]),
|
|
};
|
|
return { prisma, artifacts, groupArtifacts };
|
|
}
|
|
|
|
describe("artifact search batching", () => {
|
|
it.each([1, 25])("uses one lookup per destination for %i matching artifacts", async (count) => {
|
|
const { prisma } = fixture(count);
|
|
await querySpaceSearch(prisma as unknown as PrismaClient, actor, "report");
|
|
// Two artifact batches and the existing direct/group message searches.
|
|
expect(prisma.$queryRaw).toHaveBeenCalledTimes(4);
|
|
const direct = prisma.$queryRaw.mock.calls[0]?.[0];
|
|
const group = prisma.$queryRaw.mock.calls[1]?.[0];
|
|
expect(direct.text).toContain('t."botId" = candidate."targetId"');
|
|
expect(group.text).toContain('t."groupId" = candidate."targetId"');
|
|
for (const query of [direct, group]) {
|
|
expect(query.values).toContain(actor.spaceId);
|
|
expect(query.values).toContain(actor.userId);
|
|
expect(query.text).toContain('ORDER BY m."createdAt" DESC');
|
|
}
|
|
});
|
|
|
|
it("preserves artifact priority and skips missing messages regardless of batch row order", async () => {
|
|
const { prisma } = fixture(3);
|
|
prisma.$queryRaw
|
|
.mockResolvedValueOnce([
|
|
{ artifactId: "artifact-2", id: "message-2", seq: 8 },
|
|
{ artifactId: "artifact-0", id: "message-0", seq: 6 },
|
|
])
|
|
.mockResolvedValueOnce([{ artifactId: "group-artifact-1", id: "group-message", seq: 3 }]);
|
|
const hits = await querySpaceSearch(prisma as unknown as PrismaClient, actor, "report");
|
|
expect(hits.map(({ artifactId, messageId, seq }) => ({ artifactId, messageId, seq }))).toEqual([
|
|
{ artifactId: "artifact-0", messageId: "message-0", seq: 6 },
|
|
{ artifactId: "artifact-2", messageId: "message-2", seq: 8 },
|
|
{ artifactId: "group-artifact-1", messageId: "group-message", seq: 3 },
|
|
]);
|
|
});
|
|
|
|
it("does not issue artifact queries for empty batches", async () => {
|
|
const { prisma } = fixture(0);
|
|
expect(await querySpaceSearch(prisma as unknown as PrismaClient, actor, "report")).toEqual([]);
|
|
expect(prisma.$queryRaw).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|