@@ -0,0 +1,146 @@
|
||||
// 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",
|
||||
])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user