Files
template-app/modules/knowledge/__tests__/auto-index.test.ts
T
2026-11-14 09:56:45 +00:00

74 lines
3.9 KiB
TypeScript

// A project's automatic PDF indexing (template lesson core-11): an administrator turns it on into an
// existing collection, and is recorded as the one whose permission each indexing job re-checks; off
// is no row. Another project's collection is not found, and a project with knowledge off refuses.
import type { TenantScope } from "@conusai-kit/core"
import { createMemoryDataSource } from "@conusai-kit/data"
import { DEFAULT_FILES_POLICY, files } from "@conusai-kit/files"
import { fakeObjectStore } from "@conusai-kit/testing"
import { describe, expect, it } from "vitest"
import { autoIndex, collections, KNOWLEDGE_ENTITIES } from "../contract/index.js"
import { getAutoIndex, setAutoIndex } from "../core/auto-index.js"
import type { KnowledgeDeps } from "../core/ports.js"
const ADMIN = "01900000-0000-7000-0000-000000000009"
const scope: TenantScope = {
level: "project",
actor: { userId: ADMIN },
orgId: "01900000-0000-7000-0000-000000000001",
workspaceId: "01900000-0000-7000-0000-000000000002",
projectId: "01900000-0000-7000-0000-000000000003",
}
const otherProject: TenantScope = { ...scope, projectId: "01900000-0000-7000-0000-000000000004" }
function makeDeps(enabled = true) {
const data = createMemoryDataSource({ entities: [...KNOWLEDGE_ENTITIES, files] })
const deps: KnowledgeDeps & { now: () => number } = {
data,
files: { data, store: fakeObjectStore(), policy: DEFAULT_FILES_POLICY },
flags: { enabled: async () => enabled },
now: () => Date.parse("2026-11-09T19:00:00.000Z"),
}
return { data, deps }
}
describe("automatic PDF indexing of a project", () => {
it("is off until an administrator names a collection, and records who turned it on", async () => {
const { data, deps } = makeDeps()
await data.forScope(scope).of(collections).insert({ name: "contracts", createdBy: ADMIN })
expect(await getAutoIndex(deps, scope)).toEqual({ collectionName: null })
expect(await setAutoIndex(deps, scope, "contracts")).toEqual({ collectionName: "contracts" })
expect(await getAutoIndex(deps, scope)).toEqual({ collectionName: "contracts" })
const [row] = (await data.forScope(scope).of(autoIndex).list({ limit: 10 })).items
expect(row).toMatchObject({ collectionName: "contracts", enabledBy: ADMIN, enabledAt: "2026-11-09T19:00:00.000Z" })
// Another project of the same organization is not opted in.
expect(await getAutoIndex(deps, otherProject)).toEqual({ collectionName: null })
})
it("keeps one row per project: naming another collection moves it, and off removes it", async () => {
const { data, deps } = makeDeps()
for (const name of ["contracts", "invoices"])
await data.forScope(scope).of(collections).insert({ name, createdBy: ADMIN })
await setAutoIndex(deps, scope, "contracts")
await setAutoIndex(deps, scope, "invoices")
expect(
(await data.forScope(scope).of(autoIndex).list({ limit: 10 })).items.map((row) => row.collectionName),
).toEqual(["invoices"])
expect(await setAutoIndex(deps, scope, null)).toEqual({ collectionName: null })
expect((await data.forScope(scope).of(autoIndex).list({ limit: 10 })).items).toEqual([])
// Off when already off writes nothing and answers the same.
expect(await setAutoIndex(deps, scope, null)).toEqual({ collectionName: null })
})
it("answers another project's collection as not found, and refuses while knowledge is off", async () => {
const { data, deps } = makeDeps()
await data.forScope(otherProject).of(collections).insert({ name: "theirs", createdBy: ADMIN })
await expect(setAutoIndex(deps, scope, "theirs")).rejects.toMatchObject({ status: 404 })
const off = makeDeps(false)
await off.data.forScope(scope).of(collections).insert({ name: "contracts", createdBy: ADMIN })
await expect(setAutoIndex(off.deps, scope, "contracts")).rejects.toMatchObject({ status: 403 })
expect((await off.data.forScope(scope).of(autoIndex).list({ limit: 10 })).items).toEqual([])
})
})