Files
memind/mindspace-chat-save.test.mjs
T
John 6d99d762da Add image-designer skill, align billing to DeepSeek ×1, and enrich Plaza demo.
Introduce AI image generation with chat shortcut and agent API, improve MindSpace chat-to-page save resolution, and seed Plaza covers with production deploy scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 16:29:19 -07:00

222 lines
8.4 KiB
JavaScript

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,
extractHtmlFilenameHints,
findPublishHtml,
injectHtmlBaseHref,
resolveChatSaveAnalysis,
sanitizeMessageContentForSave,
fetchPublishHtmlFallback,
} from './mindspace-chat-save.mjs';
const USER_ID = 'a6fb1e97-2b0f-447b-b138-4561d8e5c53e';
test('extractStaticPageLinks finds user-owned MindSpace html links', () => {
const content = `
页面已完成!
https://g2.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, 'mapo-tofu.html');
});
test('extractStaticPageLinks ignores other users', () => {
const content = 'https://g2.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);
});
test('extractStaticPageLinks accepts legacy username urls when userId and username provided', () => {
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].relativePath, 'report.html');
});
test('analyzeChatMessageForSave prefers static html mode', () => {
const analysis = analyzeChatMessageForSave({
content: `链接 https://g2.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 html stored only in legacy username directory', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-chat-save-legacy-'));
const legacyDir = path.join(root, 'MindSpace', 'john', 'public');
await fs.mkdir(legacyDir, { recursive: true });
await fs.writeFile(
path.join(legacyDir, 'legacy-only.html'),
'<!DOCTYPE html><html><head><title>Legacy</title></head><body>legacy</body></html>',
'utf8',
);
const loaded = await findPublishHtml(root, USER_ID, 'public/legacy-only.html', { username: 'john' });
assert.equal(loaded.relativePath, 'public/legacy-only.html');
assert.match(loaded.content, /Legacy/);
const migrated = path.join(root, 'MindSpace', USER_ID, 'public', 'legacy-only.html');
assert.ok(await fs.stat(migrated).then(() => true).catch(() => false));
});
test('resolveChatSaveAnalysis resolves uuid link when html exists only in legacy username dir', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-chat-save-uuid-link-'));
const legacyDir = path.join(root, 'MindSpace', 'john', 'public');
await fs.mkdir(legacyDir, { recursive: true });
await fs.writeFile(
path.join(legacyDir, 'report.html'),
'<!DOCTYPE html><html><head><title>Report</title></head><body>ok</body></html>',
'utf8',
);
const content = `链接 https://g2.tkmind.cn/MindSpace/${USER_ID}/public/report.html`;
const result = await resolveChatSaveAnalysis({
content,
userId: USER_ID,
username: 'john',
h5Root: root,
});
assert.equal(result.analysis.contentMode, 'static_html');
assert.equal(result.resolvedHtml?.relativePath, 'public/report.html');
});
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'),
'<!DOCTYPE html><html><head><title>Welcome</title></head><body>hi</body></html>',
'utf8',
);
const loaded = await findPublishHtml(root, USER_ID, 'welcome.html');
assert.equal(loaded.relativePath, 'public/welcome.html');
assert.match(loaded.content, /Welcome/);
});
test('injectHtmlBaseHref adds base tag for relative assets', () => {
const html = '<html><head><title>x</title></head><body></body></html>';
const next = injectHtmlBaseHref(html, buildWorkspaceBaseHref(USER_ID, 'public/page.html'));
assert.match(next, /<base href="\/MindSpace\//);
});
test('extractHtmlFilenameHints preserves normal hyphenated filenames', () => {
const hints = extractHtmlFilenameHints('已生成 training-registration.html');
assert.deepEqual(hints, ['training-registration.html']);
});
test('sanitizeMessageContentForSave repairs duplicated streaming urls', () => {
const raw =
'httpshttps://://gg22.t.tkmkmindind.cn.cn/M/MindSpace/a6fb1e97/public/public/w/welcomeelcome-v-v22.html.html';
const cleaned = sanitizeMessageContentForSave(raw);
assert.doesNotMatch(cleaned, /httpshttps/);
assert.match(cleaned, /https:\/\//);
});
test('resolveChatSaveAnalysis falls back to local html filename hints', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-chat-save-hint-'));
const publishDir = path.join(root, 'MindSpace', USER_ID, 'public');
await fs.mkdir(publishDir, { recursive: true });
await fs.writeFile(
path.join(publishDir, 'welcome-v2.html'),
'<!DOCTYPE html><html><head><title>Welcome v2</title></head><body>hi</body></html>',
'utf8',
);
const corrupted =
'请看 welcomeelcome-v-v22.html.html,链接可能坏了 httpshttps://://broken.example/MindSpace/other/report.html';
const result = await resolveChatSaveAnalysis({
content: corrupted,
userId: USER_ID,
username: 'john',
h5Root: root,
});
assert.equal(result.analysis.contentMode, 'static_html');
assert.equal(result.resolvedHtml?.relativePath, 'public/welcome-v2.html');
});
test('fetchPublishHtmlFallback mirrors remote html into local MindSpace when disk is missing', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-chat-save-fetch-'));
const html =
'<!DOCTYPE html><html><head><title>Remote</title></head><body>from remote</body></html>';
const analysis = analyzeChatMessageForSave({
content: `https://g2.tkmind.cn/MindSpace/${USER_ID}/public/remote-page.html`,
userId: USER_ID,
username: 'john',
h5Root: root,
});
const fetched = await fetchPublishHtmlFallback(analysis, {
publicBaseUrl: 'http://127.0.0.1:8081',
fetchImpl: async () => ({
ok: true,
headers: { get: () => 'text/html' },
text: async () => html,
}),
});
assert.ok(fetched);
assert.equal(fetched.relativePath, 'public/remote-page.html');
assert.match(fetched.content, /from remote/);
const local = path.join(root, 'MindSpace', USER_ID, 'public', 'remote-page.html');
assert.match(await fs.readFile(local, 'utf8'), /from remote/);
});