Files
memind/mindspace-conversation-package-registry.test.mjs
T
2026-07-02 21:43:38 +08:00

242 lines
7.6 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import { createConversationPackageRegistry } from './mindspace-conversation-package-registry.mjs';
import { createConversationPackageStore } from './mindspace-conversation-package-store.mjs';
import { createMindSpaceServiceFacade } from './mindspace-service.mjs';
import { createLocalMindSpaceStorageAdapter } from './mindspace-storage-adapter.mjs';
function createFakePool() {
const packages = new Map();
const artifacts = new Map();
return {
async query(sql, params = []) {
if (/INSERT INTO h5_conversation_packages/i.test(sql)) {
const [
id,
user_id,
session_id,
title,
status,
storage_prefix,
manifest_asset_id,
created_at,
updated_at,
] = params;
packages.set(`${user_id}:${session_id}`, {
id,
user_id,
session_id,
title,
status,
storage_prefix,
manifest_asset_id,
created_at,
updated_at,
});
return [{ affectedRows: 1 }];
}
if (/FROM h5_conversation_packages/i.test(sql)) {
return [[packages.get(`${params[0]}:${params[1]}`)].filter(Boolean)];
}
if (/INSERT INTO h5_conversation_artifacts/i.test(sql)) {
const [
id,
package_id,
artifact_kind,
role,
asset_id,
page_id,
publication_id,
agent_run_id,
message_id,
display_name,
mime_type,
size_bytes,
storage_key,
canonical_url,
sort_order,
created_at,
] = params;
artifacts.set(id, {
id,
package_id,
artifact_kind,
role,
asset_id,
page_id,
publication_id,
agent_run_id,
message_id,
display_name,
mime_type,
size_bytes,
storage_key,
canonical_url,
sort_order,
created_at,
});
return [{ affectedRows: 1 }];
}
if (/FROM h5_conversation_artifacts/i.test(sql)) {
return [
[...artifacts.values()]
.filter((row) => row.package_id === params[0])
.sort(
(a, b) =>
Number(a.sort_order) - Number(b.sort_order) ||
Number(a.created_at) - Number(b.created_at) ||
String(a.id).localeCompare(String(b.id)),
),
];
}
throw new Error(`unexpected query: ${sql}`);
},
};
}
test('createConversationPackageRegistry validates required collaborators', () => {
assert.throws(
() => createConversationPackageRegistry({ store: {}, service: {} }),
(error) => error.code === 'missing_conversation_package_store',
);
});
test('conversation package registry records artifacts and writes manifest through storage facade', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mindspace-registry-'));
const service = createMindSpaceServiceFacade({
storageAdapter: createLocalMindSpaceStorageAdapter(root),
publicBaseUrl: 'https://m.tkmind.cn',
});
const store = createConversationPackageStore(createFakePool());
const registry = createConversationPackageRegistry({ store, service });
const packageRecord = await registry.ensurePackage({
userId: 'user-1',
sessionId: 'session-1',
title: '研究简报',
now: 1000,
});
await registry.recordArtifact({
id: 'ca_page',
packageId: packageRecord.id,
artifactKind: 'public_html',
displayName: 'brief.html',
canonicalUrl: 'https://m.tkmind.cn/MindSpace/user-1/public/brief.html',
sortOrder: 1,
now: 2000,
});
const result = await registry.writeManifestForSession({
userId: 'user-1',
sessionId: 'session-1',
});
assert.equal(result.storageKey, 'users/user-1/conversations/session-1/manifest.json');
const manifestJson = await service.getPackageObject({ packageRecord, relativePath: 'manifest.json' });
const manifest = JSON.parse(manifestJson.toString('utf8'));
assert.equal(manifest.title, '研究简报');
assert.deepEqual(manifest.artifacts, [
{
artifactId: 'ca_page',
kind: 'public_html',
displayName: 'brief.html',
canonicalUrl: 'https://m.tkmind.cn/MindSpace/user-1/public/brief.html',
sortOrder: 1,
createdAt: 2000,
},
]);
});
test('conversation package registry reads manifest without writing storage', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mindspace-registry-'));
const service = createMindSpaceServiceFacade({
storageAdapter: createLocalMindSpaceStorageAdapter(root),
publicBaseUrl: 'https://m.tkmind.cn',
});
const store = createConversationPackageStore(createFakePool());
const registry = createConversationPackageRegistry({ store, service });
const packageRecord = await registry.ensurePackage({
userId: 'user-1',
sessionId: 'session-1',
title: '输入与页面',
now: 1000,
});
await registry.recordArtifact({
id: 'ca_image',
packageId: packageRecord.id,
artifactKind: 'input_image',
displayName: 'cover.png',
sortOrder: 1,
now: 1000,
});
const manifest = await registry.readManifestForSession({
userId: 'user-1',
sessionId: 'session-1',
});
assert.equal(manifest.packageId, 'cp_session-1');
assert.equal(manifest.title, '输入与页面');
assert.deepEqual(
manifest.artifacts.map((item) => item.kind),
['input_image'],
);
assert.equal(
await service.statPackageObject({ packageRecord, relativePath: 'manifest.json' }).then((stat) => stat.exists),
false,
);
});
test('conversation package registry stores and reads private artifact objects', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mindspace-registry-'));
const service = createMindSpaceServiceFacade({
storageAdapter: createLocalMindSpaceStorageAdapter(root),
publicBaseUrl: 'https://m.tkmind.cn',
});
const store = createConversationPackageStore(createFakePool());
const registry = createConversationPackageRegistry({ store, service });
const { packageRecord, writeResult } = await registry.putObjectForSession({
userId: 'user-1',
sessionId: 'session-1',
title: '导出文档',
relativePath: 'artifacts/report.docx',
body: Buffer.from('docx-body'),
});
const artifact = await registry.recordArtifact({
id: 'ca_docx',
packageId: packageRecord.id,
artifactKind: 'docx',
displayName: 'report.docx',
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
storageKey: writeResult.key,
sortOrder: 10,
now: 2000,
});
const result = await registry.readArtifactObject({
userId: 'user-1',
sessionId: 'session-1',
artifactId: artifact.id,
});
assert.equal(result.artifact.displayName, 'report.docx');
assert.equal(result.body.toString('utf8'), 'docx-body');
});
test('conversation package registry skips manifest writes for unknown sessions', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'mindspace-registry-'));
const registry = createConversationPackageRegistry({
store: createConversationPackageStore(createFakePool()),
service: createMindSpaceServiceFacade({
storageAdapter: createLocalMindSpaceStorageAdapter(root),
publicBaseUrl: 'https://m.tkmind.cn',
}),
});
assert.equal(await registry.writeManifestForSession({ userId: 'user-1', sessionId: 'missing' }), null);
});