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 });
}
});