@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// A delivery's project comes from the record, inside the delivery's organization (adapters/jobs.ts). The record's
|
||||
// workspace and project must exist in that organization before knowledge acts on them; one that names another
|
||||
// workspace's project, or one the organization does not have, is refused by name and nothing is enqueued.
|
||||
|
||||
import { Container, type EventContext, newId, type OrgScope } from "@conusai-kit/core"
|
||||
import { createMemoryDataSource, DATA_SOURCE, JOB_QUEUE } from "@conusai-kit/data"
|
||||
import { projects, workspaces } from "@conusai-kit/tenancy"
|
||||
import { fakeJobQueue } from "@conusai-kit/testing"
|
||||
import { describe, expect, it } from "vitest"
|
||||
import { KNOWLEDGE_SUBSCRIPTIONS } from "../adapters/jobs.js"
|
||||
import { KNOWLEDGE_ENTITIES, KNOWLEDGE_SCOPE } from "../index.js"
|
||||
|
||||
async function delivery(archivedAt: string | null = null) {
|
||||
const data = createMemoryDataSource({ entities: [workspaces, projects, ...KNOWLEDGE_ENTITIES] })
|
||||
const orgId = newId()
|
||||
const org: OrgScope = { level: "org", actor: { userId: newId() }, orgId }
|
||||
const workspace = await data
|
||||
.forScope(org)
|
||||
.of(workspaces)
|
||||
.insert({ slug: "main", name: "Main", isDefault: true, visibility: "org" })
|
||||
const other = await data
|
||||
.forScope(org)
|
||||
.of(workspaces)
|
||||
.insert({ slug: "other", name: "Other", isDefault: false, visibility: "org" })
|
||||
const project = await data
|
||||
.forScope({ level: "workspace", actor: org.actor, orgId, workspaceId: workspace.id })
|
||||
.of(projects)
|
||||
.insert({ slug: "site", name: "Site", moduleKey: "notes", archivedAt })
|
||||
const queue = fakeJobQueue()
|
||||
const container = new Container()
|
||||
container.value(DATA_SOURCE, data)
|
||||
container.value(JOB_QUEUE, queue)
|
||||
// The relay's delivery: the organization's scope, with a system actor.
|
||||
container.value(KNOWLEDGE_SCOPE, org)
|
||||
const ctx = { container } as unknown as EventContext
|
||||
return { ctx, queue, orgId, workspaceId: workspace.id, otherWorkspaceId: other.id, projectId: project.id }
|
||||
}
|
||||
|
||||
const indexRequested = KNOWLEDGE_SUBSCRIPTIONS[1]
|
||||
const request = (ids: { orgId: string; workspaceId: string; projectId: string }) => ({
|
||||
name: "knowledge.DocumentIndexRequested",
|
||||
payload: { ...ids, documentId: newId(), collectionName: "contracts", fileId: newId() },
|
||||
})
|
||||
|
||||
describe("a knowledge delivery acts only in a project of its organization", () => {
|
||||
it("enqueues the job in the record's project when it exists there", async () => {
|
||||
const { ctx, queue, orgId, workspaceId, projectId } = await delivery()
|
||||
await indexRequested?.handler(request({ orgId, workspaceId, projectId }) as never, ctx)
|
||||
expect(queue.of("knowledge.index")).toHaveLength(1)
|
||||
})
|
||||
|
||||
it("refuses, by name, a workspace the organization does not have", async () => {
|
||||
const { ctx, queue, orgId, projectId } = await delivery()
|
||||
const workspaceId = newId()
|
||||
await expect(indexRequested?.handler(request({ orgId, workspaceId, projectId }) as never, ctx)).rejects.toThrow(
|
||||
new RegExp(`workspace ${workspaceId}`),
|
||||
)
|
||||
expect(queue.of("knowledge.index")).toEqual([])
|
||||
})
|
||||
|
||||
it("refuses, by name, a project that is not in the record's workspace", async () => {
|
||||
const { ctx, queue, orgId, otherWorkspaceId, projectId } = await delivery()
|
||||
await expect(
|
||||
indexRequested?.handler(request({ orgId, workspaceId: otherWorkspaceId, projectId }) as never, ctx),
|
||||
).rejects.toThrow(new RegExp(`project ${projectId}`))
|
||||
expect(queue.of("knowledge.index")).toEqual([])
|
||||
})
|
||||
|
||||
it("acts on a record of an archived project: an archive hides a project and does not end its work", async () => {
|
||||
// The kit's one rule (kit-tenancy suspension.ts): an archived project refuses a person's write, not work with
|
||||
// no person behind it. A deleted file's text must still leave the index, and an index request a member made
|
||||
// before the archive still completes.
|
||||
const { ctx, queue, orgId, workspaceId, projectId } = await delivery(new Date("2026-11-09T00:00:00Z").toISOString())
|
||||
await indexRequested?.handler(request({ orgId, workspaceId, projectId }) as never, ctx)
|
||||
expect(queue.of("knowledge.index")).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user