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 { createPageService, pageInternals, normalizeWorkspaceRelativePath } from './mindspace-pages.mjs'; import { PUBLISH_ROOT_DIR } from './user-publish.mjs'; test('parseJsonColumn accepts mysql json objects and strings', () => { const objectValue = { relative_path: 'public/demo.html', auto_synced: true }; assert.deepEqual(pageInternals.parseJsonColumn(objectValue), objectValue); assert.deepEqual( pageInternals.parseJsonColumn(JSON.stringify(objectValue)), objectValue, ); assert.deepEqual(pageInternals.parseJsonColumn(null, { ok: true }), { ok: true }); }); test('normalizePageInput trims values and constrains templates', () => { const result = pageInternals.normalizePageInput({ title: ' 页面标题 ', content: ' 页面正文 ', templateId: 'unknown-template', }); assert.equal(result.title, '页面标题'); assert.equal(result.content, '页面正文'); assert.equal(result.templateId, 'editorial'); assert.equal(result.contentBytes, Buffer.byteLength('页面正文')); }); test('normalizePageInput rejects empty and oversized page content', () => { assert.throws( () => pageInternals.normalizePageInput({ title: '', content: '正文' }), { code: 'invalid_page_input' }, ); assert.throws( () => pageInternals.normalizePageInput({ title: '页面', content: '文'.repeat(400_000), }), { code: 'page_content_too_large' }, ); }); test('normalizePageInput maps html content to static-html template', () => { const result = pageInternals.normalizePageInput({ title: '页面', content: 'Hi', contentFormat: 'html', }); assert.equal(result.contentFormat, 'html'); assert.equal(result.templateId, 'static-html'); assert.equal(result.pageType, 'html'); }); test('renderHtmlPreview injects restrictive CSP', () => { const html = pageInternals.renderHtmlPreview( '

Hi

', ); assert.match(html, /Content-Security-Policy/); assert.match(html, /script-src 'none'/); }); test('renderPublicationHtml serves html pages without escaping markup', () => { const html = pageInternals.renderPublicationHtml({ page_type: 'html', title: '麻婆豆腐', summary: '川菜', template_id: 'static-html', version_no: 1, content: '麻婆豆腐

麻婆豆腐

', }); assert.match(html, /

麻婆豆腐<\/h1>/); assert.doesNotMatch(html, /<h1>/); assert.doesNotMatch(html, /MINDSPACE/); }); test('normalizeListPageFilters clamps limit and offset', () => { assert.deepEqual(pageInternals.normalizeListPageFilters({ limit: 999, offset: -5 }), { limit: 100, offset: 0, }); assert.deepEqual(pageInternals.normalizeListPageFilters({ limit: 15, offset: 30 }), { limit: 15, offset: 30, }); }); test('preview rendering escapes active HTML and emits a restrictive CSP', () => { const html = pageInternals.renderPreviewHtml({ title: '', summary: '', content: '# 标题\n\n', templateId: 'report', versionNo: 2, }); assert.match(html, /Content-Security-Policy/); assert.match(html, /default-src 'none'/); assert.doesNotMatch(html, /