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 { backfillConversationPackageArtifacts } from './mindspace-conversation-package-backfill.mjs'; test('backfillConversationPackageArtifacts registers pages publications and completed uploads', async () => { const storageRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'conversation-backfill-storage-')); const h5Root = await fs.mkdtemp(path.join(os.tmpdir(), 'conversation-backfill-h5-')); const publicationStorageKey = 'users/user-1/publications/pub-1/index.html'; await fs.mkdir(path.dirname(path.join(storageRoot, publicationStorageKey)), { recursive: true }); await fs.writeFile(path.join(storageRoot, publicationStorageKey), 'Published'); await fs.mkdir(path.join(h5Root, 'MindSpace', 'user-1', 'public'), { recursive: true }); await fs.writeFile(path.join(h5Root, 'MindSpace', 'user-1', 'public', 'published.docx'), 'docx'); const calls = []; const registry = { async ensurePackage(input) { calls.push(['ensurePackage', input]); return { id: 'cp-session-1' }; }, async recordArtifact(input) { calls.push(['recordArtifact', input]); return input; }, async writeManifestForSession(input) { calls.push(['writeManifestForSession', input]); return input; }, }; const pool = { async query(sql, params) { assert.equal(params[0], 'user-1'); assert.equal(params[1], 'session-1'); if (sql.includes('FROM h5_page_records p') && sql.includes('LEFT JOIN h5_page_versions')) { return [[ { id: 'page-1', title: 'Draft page', source_message_id: 'message-1', current_version_id: 'version-1', version_no: 3, content_asset_id: 'asset-page', mime_type: 'text/html', size_bytes: 456, storage_key: 'users/user-1/pages/page-1/versions/v1.html', }, ]]; } if (sql.includes('FROM h5_publish_records pr')) { return [[ { id: 'pub-1', page_id: 'page-1', public_url: 'https://m.tkmind.cn/u/john/pages/published', published_at: 3000, title: 'Published', source_message_id: 'message-1', bundle_asset_id: 'asset-pub', source_relative_path: 'public/published.html', storage_key: publicationStorageKey, size_bytes: 789, }, ]]; } if (sql.includes('FROM h5_upload_sessions u')) { return [[ { id: 'upload-1', filename: 'input.png', source_message_id: 'message-2', completed_asset_id: 'asset-upload', detected_mime_type: 'image/png', actual_size: 123, mime_type: 'image/png', original_filename: 'input.png', display_name: 'input.png', size_bytes: 123, storage_key: 'users/user-1/public/images/input.png', }, ]]; } return [[]]; }, }; try { const result = await backfillConversationPackageArtifacts({ pool, registry, storageRoot, h5Root, user: { id: 'user-1' }, sessionId: 'session-1', now: 1000, }); assert.deepEqual(result, { pages: 1, publications: 1, uploads: 1 }); const kinds = calls .filter(([name]) => name === 'recordArtifact') .map(([, input]) => input.artifactKind) .sort(); assert.deepEqual(kinds, ['docx', 'input_image', 'page', 'public_html']); const upload = calls.find(([, input]) => input?.artifactKind === 'input_image')?.[1]; assert.equal(upload.messageId, 'message-2'); assert.equal(upload.canonicalUrl, '/api/mindspace/v1/assets/asset-upload/download'); } finally { await fs.rm(storageRoot, { recursive: true, force: true }); await fs.rm(h5Root, { recursive: true, force: true }); } });