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

158 lines
4.4 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { createConversationPackageStore } from './mindspace-conversation-package-store.mjs';
function createFakePool() {
const packages = new Map();
const artifacts = new Map();
const queries = [];
return {
queries,
async query(sql, params = []) {
queries.push({ 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)) {
const packageId = params[0];
const rows = [...artifacts.values()]
.filter((row) => row.package_id === packageId)
.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)),
);
return [rows];
}
throw new Error(`unexpected query: ${sql}`);
},
};
}
test('createConversationPackageStore requires a queryable pool', () => {
assert.throws(
() => createConversationPackageStore({}),
(error) => error.code === 'missing_query_pool',
);
});
test('conversation package store upserts package metadata and builds manifest', async () => {
const pool = createFakePool();
const store = createConversationPackageStore(pool);
const packageRecord = await store.upsertPackage({
userId: 'user-1',
sessionId: 'session-1',
title: '旅行计划',
now: 1000,
});
assert.equal(packageRecord.id, 'cp_session-1');
assert.equal(packageRecord.storagePrefix, 'users/user-1/conversations/session-1');
assert.match(pool.queries[0].sql, /ON DUPLICATE KEY UPDATE/);
await store.upsertArtifact({
id: 'ca_late',
packageId: packageRecord.id,
artifactKind: 'public_html',
displayName: 'plan.html',
storageKey: 'users/user-1/conversations/session-1/public/plan.html',
canonicalUrl: 'https://m.tkmind.cn/MindSpace/user-1/public/plan.html',
sortOrder: 2,
now: 3000,
});
await store.upsertArtifact({
id: 'ca_first',
packageId: packageRecord.id,
artifactKind: 'input_image',
displayName: 'cover.png',
sortOrder: 1,
now: 2000,
});
const manifest = await store.buildManifestForSession({
userId: 'user-1',
sessionId: 'session-1',
});
assert.equal(manifest.packageId, 'cp_session-1');
assert.equal(manifest.uri, 'mindspace://users/user-1/conversations/session-1');
assert.deepEqual(
manifest.artifacts.map((artifact) => artifact.artifactId),
['ca_first', 'ca_late'],
);
assert.equal(
manifest.artifacts[1].canonicalUrl,
'https://m.tkmind.cn/MindSpace/user-1/public/plan.html',
);
});
test('conversation package store returns null manifest for missing session', async () => {
const store = createConversationPackageStore(createFakePool());
assert.equal(await store.buildManifestForSession({ userId: 'user-1', sessionId: 'missing' }), null);
});