b273bb7aa2
Add a MindSpace public HTML checker for missing companion docx/PDF paths, wire it into runtime packaging and release-portal-runtime-prod.sh, and document the workflow for agents and ops. Co-authored-by: Cursor <cursoragent@cursor.com>
75 lines
3.3 KiB
JavaScript
75 lines
3.3 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import {
|
|
collectPublicHtmlReferences,
|
|
findMissingPublicHtmlReferences,
|
|
isDownloadLikeReference,
|
|
isFilesystemPublicReference,
|
|
scanPublicHtmlLinks,
|
|
} from './mindspace-public-links.mjs';
|
|
|
|
test('isFilesystemPublicReference skips external and absolute server paths', () => {
|
|
assert.equal(isFilesystemPublicReference('duck.docx'), true);
|
|
assert.equal(isFilesystemPublicReference('assets/hero.jpg'), true);
|
|
assert.equal(isFilesystemPublicReference('https://example.com/a.docx'), false);
|
|
assert.equal(isFilesystemPublicReference('/api/mindspace/v1/assets/a/download'), false);
|
|
assert.equal(isFilesystemPublicReference('/MindSpace/u/public/page.html'), false);
|
|
});
|
|
|
|
test('isDownloadLikeReference matches download attr and attachment extensions', () => {
|
|
assert.equal(isDownloadLikeReference('report.docx', 'href="report.docx" download'), true);
|
|
assert.equal(isDownloadLikeReference('assets/chart.png', 'src="assets/chart.png"'), false);
|
|
assert.equal(isDownloadLikeReference('data/report.pdf', 'href="data/report.pdf"'), true);
|
|
});
|
|
|
|
test('collectPublicHtmlReferences downloads-only ignores api src and cover', () => {
|
|
const html = `<!doctype html>
|
|
<head>
|
|
<meta name="mindspace-cover" content='{"cover":"assets/hero.jpg"}'>
|
|
</head>
|
|
<body>
|
|
<a href="report.docx" download>下载</a>
|
|
<img src="/api/mindspace/v1/assets/a/download?inline=1">
|
|
<a href="assets/chart.png">chart</a>
|
|
</body>`;
|
|
const all = collectPublicHtmlReferences(html, '/tmp/public');
|
|
const downloads = collectPublicHtmlReferences(html, '/tmp/public', { downloadsOnly: true });
|
|
assert.deepEqual(
|
|
all.map((item) => item.ref).sort(),
|
|
['assets/chart.png', 'assets/hero.jpg', 'report.docx'],
|
|
);
|
|
assert.deepEqual(downloads.map((item) => item.ref), ['report.docx']);
|
|
});
|
|
|
|
test('findMissingPublicHtmlReferences reports missing sibling files', () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ms-links-'));
|
|
const htmlPath = path.join(dir, 'report.html');
|
|
fs.writeFileSync(htmlPath, '<a href="report.docx" download>下载</a>');
|
|
const missing = findMissingPublicHtmlReferences(htmlPath, null, { downloadsOnly: true });
|
|
assert.equal(missing.length, 1);
|
|
assert.equal(missing[0].ref, 'report.docx');
|
|
|
|
fs.writeFileSync(path.join(dir, 'report.docx'), 'doc');
|
|
assert.deepEqual(findMissingPublicHtmlReferences(htmlPath, null, { downloadsOnly: true }), []);
|
|
});
|
|
|
|
test('normalizeReferencePath decodes percent-encoded filenames', () => {
|
|
const html = '<a href="%E5%85%BB%E7%8C%AA.docx" download>x</a>';
|
|
const refs = collectPublicHtmlReferences(html, '/tmp/public', { downloadsOnly: true });
|
|
assert.equal(refs[0]?.ref, '养猪.docx');
|
|
});
|
|
|
|
test('scanPublicHtmlLinks walks MindSpace user public html files', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ms-root-'));
|
|
const publicDir = path.join(root, 'user-a', 'public');
|
|
fs.mkdirSync(publicDir, { recursive: true });
|
|
fs.writeFileSync(path.join(publicDir, 'page.html'), '<a href="missing.pdf" download>pdf</a>');
|
|
const issues = scanPublicHtmlLinks(root, { downloadsOnly: true });
|
|
assert.equal(issues.length, 1);
|
|
assert.match(issues[0].htmlPath, /page\.html$/);
|
|
assert.equal(issues[0].ref, 'missing.pdf');
|
|
});
|