import assert from 'node:assert/strict'; import test from 'node:test'; import { assertConversationArtifactKind, buildConversationPackageManifest, buildConversationPackageUri, buildConversationStoragePrefix, createConversationArtifact, createConversationPackageRecord, } from './mindspace-conversation-package.mjs'; test('buildConversationStoragePrefix produces backend-neutral object prefixes', () => { assert.equal( buildConversationStoragePrefix({ userId: 'user-1', sessionId: 'session-1' }), 'users/user-1/conversations/session-1', ); assert.equal( buildConversationPackageUri({ userId: 'user 1', sessionId: 'session:1' }), 'mindspace://users/user%201/conversations/session%3A1', ); }); test('buildConversationStoragePrefix rejects path separators', () => { assert.throws( () => buildConversationStoragePrefix({ userId: '../user', sessionId: 'session-1' }), (error) => error.code === 'invalid_storage_segment', ); assert.throws( () => buildConversationStoragePrefix({ userId: 'user-1', sessionId: 'a/b' }), (error) => error.code === 'invalid_storage_segment', ); }); test('createConversationPackageRecord defaults storage prefix and timestamps', () => { assert.deepEqual( createConversationPackageRecord({ id: 'cp_custom', userId: 'user-1', sessionId: 'session-1', title: '旅行页面', now: 1000, }), { id: 'cp_custom', userId: 'user-1', sessionId: 'session-1', title: '旅行页面', status: 'active', storagePrefix: 'users/user-1/conversations/session-1', manifestAssetId: null, createdAt: 1000, updatedAt: 1000, }, ); }); test('createConversationArtifact validates artifact kind and preserves provenance', () => { assert.equal(assertConversationArtifactKind('public_html'), 'public_html'); assert.throws( () => assertConversationArtifactKind('random_blob'), (error) => error.code === 'unsupported_conversation_artifact_kind', ); const artifact = createConversationArtifact({ id: 'ca_1', packageId: 'cp_1', artifactKind: 'public_html', assetId: 'asset-1', pageId: 'page-1', publicationId: 'pub-1', agentRunId: 'run-1', messageId: 'msg-1', displayName: 'report.html', mimeType: 'text/html', sizeBytes: 120, storageKey: 'users/user-1/conversations/session-1/public/report.html', canonicalUrl: 'https://m.tkmind.cn/MindSpace/user-1/public/report.html', sortOrder: 2, now: 2000, }); assert.equal(artifact.packageId, 'cp_1'); assert.equal(artifact.artifactKind, 'public_html'); assert.equal(artifact.assetId, 'asset-1'); assert.equal(artifact.pageId, 'page-1'); assert.equal(artifact.publicationId, 'pub-1'); assert.equal(artifact.messageId, 'msg-1'); }); test('buildConversationPackageManifest sorts artifacts and emits stable public shape', () => { const manifest = buildConversationPackageManifest({ packageRecord: { id: 'cp_1', userId: 'user-1', sessionId: 'session-1', title: '项目周报', now: 1000, }, artifacts: [ { id: 'ca_late', artifactKind: 'generated_file', displayName: 'notes.md', sortOrder: 2, createdAt: 3000, }, { id: 'ca_first', artifactKind: 'input_image', displayName: 'cover.png', sortOrder: 1, createdAt: 2000, }, ], }); assert.equal(manifest.schemaVersion, 1); assert.equal(manifest.uri, 'mindspace://users/user-1/conversations/session-1'); assert.equal(manifest.storagePrefix, 'users/user-1/conversations/session-1'); assert.deepEqual( manifest.artifacts.map((artifact) => artifact.artifactId), ['ca_first', 'ca_late'], ); assert.deepEqual(manifest.artifacts[0], { artifactId: 'ca_first', kind: 'input_image', displayName: 'cover.png', sortOrder: 1, createdAt: 2000, }); });