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 { analyzeChatMessageForSave, buildChatSavePreviewFrameUrl, buildChatSaveThumbnailUrl, buildWorkspaceAssetUrl, buildWorkspaceBaseHref, buildWorkspaceThumbnailUrl, extractStaticPageLinks, findPublishHtml, injectHtmlBaseHref, materializePrivateAssetsInWorkspaceHtml, materializePrivateAssetsInPublicHtmlFiles, repairMissingHtmlAssetReferences, resolveClosestHtmlRelativePath, resolveChatSaveAnalysis, sanitizeMessageContentForSave, } from './mindspace-chat-save.mjs'; const USER_ID = 'a6fb1e97-2b0f-447b-b138-4561d8e5c53e'; test('extractStaticPageLinks finds user-owned MindSpace html links', () => { const content = ` 页面已完成! https://m.tkmind.cn/MindSpace/${USER_ID}/mapo-tofu.html `; const links = extractStaticPageLinks(content, { userId: USER_ID }); assert.equal(links.length, 1); assert.equal(links[0].filename, 'mapo-tofu.html'); assert.equal(links[0].relativePath, 'public/mapo-tofu.html'); assert.equal(links[0].publicUrl, `https://m.tkmind.cn/MindSpace/${USER_ID}/public/mapo-tofu.html`); }); test('extractStaticPageLinks ignores other users', () => { const content = 'https://m.tkmind.cn/MindSpace/other-id/report.html'; const links = extractStaticPageLinks(content, { userId: USER_ID }); assert.equal(links.length, 0); }); test('extractStaticPageLinks accepts legacy username urls when username provided', () => { const content = 'https://goo.tkmind.cn/MindSpace/john/report.html'; const links = extractStaticPageLinks(content, { username: 'john' }); assert.equal(links.length, 1); assert.equal(links[0].relativePath, 'public/report.html'); assert.equal(links[0].publicUrl, 'https://goo.tkmind.cn/MindSpace/john/public/report.html'); }); test('extractStaticPageLinks accepts uuid links when both user id and username are provided', () => { const content = `https://goo.tkmind.cn/MindSpace/${USER_ID}/public/report.html`; const links = extractStaticPageLinks(content, { userId: USER_ID, username: 'john' }); assert.equal(links.length, 1); assert.equal(links[0].relativePath, 'public/report.html'); }); test('extractStaticPageLinks accepts legacy username links when user id is available', () => { const content = 'https://goo.tkmind.cn/MindSpace/john/report.html'; const links = extractStaticPageLinks(content, { userId: USER_ID, username: 'john' }); assert.equal(links.length, 1); assert.equal(links[0].publicUrl, 'https://goo.tkmind.cn/MindSpace/john/public/report.html'); }); test('analyzeChatMessageForSave prefers static html mode', () => { const analysis = analyzeChatMessageForSave({ content: `链接 https://m.tkmind.cn/MindSpace/${USER_ID}/report.html`, userId: USER_ID, username: 'john', h5Root: '/tmp/h5', }); assert.equal(analysis.contentMode, 'static_html'); assert.equal(analysis.filename, 'report.html'); assert.match(analysis.suggestedTitle, /report/i); }); test('analyzeChatMessageForSave falls back to markdown article', () => { const analysis = analyzeChatMessageForSave({ content: '这是一段普通 AI 回答,没有页面链接。', userId: USER_ID, username: 'john', h5Root: '/tmp/h5', }); assert.equal(analysis.contentMode, 'markdown'); assert.equal(analysis.links.length, 0); assert.match(analysis.suggestedTitle, /普通 AI 回答/); }); test('buildWorkspaceAssetUrl and buildWorkspaceThumbnailUrl', () => { assert.equal( buildWorkspaceAssetUrl(USER_ID, 'mapo-tofu.html'), `/MindSpace/${USER_ID}/mapo-tofu.html`, ); assert.equal( buildWorkspaceThumbnailUrl(USER_ID, 'reports/guide.html'), `/MindSpace/${USER_ID}/reports/guide.thumbnail.svg`, ); }); test('buildChatSavePreviewUrls use local api routes', () => { assert.match( buildChatSavePreviewFrameUrl({ sessionId: 'sess-1', messageId: 'msg-1', selectedLinkIndex: 2, }), /^\/api\/mindspace\/v1\/pages\/chat-save-preview\?/, ); assert.match( buildChatSaveThumbnailUrl({ sessionId: 'sess-1', messageId: 'msg-1', previewTitle: '标题', }), /chat-save-thumbnail\?.*preview_title=%E6%A0%87%E9%A2%98/, ); }); test('findPublishHtml resolves nested public html by basename', async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-chat-save-')); const publishDir = path.join(root, 'MindSpace', USER_ID, 'public'); await fs.mkdir(publishDir, { recursive: true }); await fs.writeFile( path.join(publishDir, 'welcome.html'), '