147 lines
4.8 KiB
TypeScript
147 lines
4.8 KiB
TypeScript
// The archive use cases over a recording port: what each asks the service, with no client and no network.
|
|
|
|
import type { ProjectScope } 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 { addVersion, listDocuments, registerDocument, requestKey, versionFacts } from "../core/archive.js"
|
|
import { type ArchiveService, archiveReadsOf } from "../core/ports.js"
|
|
|
|
const scope: ProjectScope = {
|
|
level: "project",
|
|
actor: { userId: "01900000-0000-7000-0000-000000000009" },
|
|
orgId: "01900000-0000-7000-0000-000000000001",
|
|
workspaceId: "01900000-0000-7000-0000-000000000002",
|
|
projectId: "01900000-0000-7000-0000-000000000003",
|
|
}
|
|
|
|
const receipt = { documentId: "doc_1", revision: "1" }
|
|
|
|
function recording() {
|
|
const asked: Array<[string, unknown]> = []
|
|
const service: ArchiveService = {
|
|
listDocuments: async (query) => {
|
|
asked.push(["listDocuments", query])
|
|
return { rows: [] }
|
|
},
|
|
getDocument: async () => {
|
|
throw new Error("not asked")
|
|
},
|
|
listVersions: async () => ({ rows: [] }),
|
|
listRetentionClasses: async () => ({ rows: [] }),
|
|
listDisposalDue: async () => ({ rows: [] }),
|
|
registerDocument: async (input) => {
|
|
asked.push(["registerDocument", input])
|
|
return receipt
|
|
},
|
|
addVersion: async (input) => {
|
|
asked.push(["addVersion", input])
|
|
return receipt
|
|
},
|
|
setLegalHold: async () => receipt,
|
|
disposeDocument: async () => receipt,
|
|
createRetentionClass: async () => undefined,
|
|
}
|
|
return { service, asked }
|
|
}
|
|
|
|
async function storedFile() {
|
|
const data = createMemoryDataSource({ entities: [files] })
|
|
const deps = { data, store: fakeObjectStore(), policy: DEFAULT_FILES_POLICY }
|
|
const row = await data
|
|
.forScope(scope)
|
|
.of(files)
|
|
.insert({
|
|
status: "stored",
|
|
storageKey: "files/w/p/f",
|
|
filename: "record.pdf",
|
|
contentType: "application/pdf",
|
|
size: 1234,
|
|
sha256: "a".repeat(64),
|
|
uploadedBy: scope.actor.userId,
|
|
storedAt: "2026-11-02T10:00:00.000Z",
|
|
uploadId: null,
|
|
declaredSize: null,
|
|
partSize: null,
|
|
deletingAt: null,
|
|
})
|
|
return { deps, fileId: row.id }
|
|
}
|
|
|
|
describe("the archive use cases", () => {
|
|
it("list documents with a trimmed search and the module's page size", async () => {
|
|
const { service, asked } = recording()
|
|
await listDocuments(service, { q: " invoice " })
|
|
await listDocuments(service, { q: " " })
|
|
expect(asked).toEqual([
|
|
["listDocuments", { q: "invoice", limit: 50 }],
|
|
["listDocuments", { limit: 50 }],
|
|
])
|
|
})
|
|
|
|
it("take a version's facts from the stored file, with an opaque Blob reference", async () => {
|
|
const { deps, fileId } = await storedFile()
|
|
expect(await versionFacts(deps, scope, fileId)).toEqual({
|
|
sha256: "a".repeat(64),
|
|
mediaType: "application/pdf",
|
|
size: 1234,
|
|
blobRef: `conusai://blob/files/${fileId}`,
|
|
})
|
|
})
|
|
|
|
it("register a document with the project's request key and a trimmed title, and no kind unless asked", async () => {
|
|
const { deps, fileId } = await storedFile()
|
|
const { service, asked } = recording()
|
|
await registerDocument(service, deps, scope, {
|
|
title: " Signed contract ",
|
|
kind: " ",
|
|
retentionClass: "contracts",
|
|
fileId,
|
|
requestId: "r1",
|
|
})
|
|
expect(asked).toEqual([
|
|
[
|
|
"registerDocument",
|
|
{
|
|
title: "Signed contract",
|
|
retentionClass: "contracts",
|
|
version: await versionFacts(deps, scope, fileId),
|
|
key: requestKey(scope, "r1"),
|
|
},
|
|
],
|
|
])
|
|
})
|
|
|
|
it("add a version over the revision the page read", async () => {
|
|
const { deps, fileId } = await storedFile()
|
|
const { service, asked } = recording()
|
|
await addVersion(service, deps, scope, { documentId: "doc_1", revision: "4", fileId, requestId: "r2" })
|
|
expect(asked[0]?.[1]).toMatchObject({ documentId: "doc_1", revision: "4", key: requestKey(scope, "r2") })
|
|
})
|
|
|
|
it("refuse a file this project does not hold before the service is asked", async () => {
|
|
const { deps } = await storedFile()
|
|
const { service, asked } = recording()
|
|
await expect(
|
|
registerDocument(service, deps, scope, {
|
|
title: "Someone else's",
|
|
retentionClass: "contracts",
|
|
fileId: "01900000-0000-7000-0000-0000000000ff",
|
|
requestId: "r3",
|
|
}),
|
|
).rejects.toMatchObject({ problem: { status: 404 } })
|
|
expect(asked).toEqual([])
|
|
})
|
|
|
|
it("hand a read handler a view without a single write", () => {
|
|
expect(Object.keys(archiveReadsOf(recording().service)).sort()).toEqual([
|
|
"getDocument",
|
|
"listDisposalDue",
|
|
"listDocuments",
|
|
"listRetentionClasses",
|
|
"listVersions",
|
|
])
|
|
})
|
|
})
|