179 lines
6.8 KiB
JavaScript
179 lines
6.8 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 {
|
|
buildMindSpaceCanonicalRedirect,
|
|
decodeMindSpaceRouteSegment,
|
|
recoverMisplacedMindSpacePublicHtml,
|
|
resolveMindSpacePublicRequest,
|
|
resolveMindSpaceHtmlFallbackRedirect,
|
|
resolveMindSpacePublishOwnerKey,
|
|
resolveMindSpacePublicRouteTarget,
|
|
resolveMindSpaceSimilarHtmlRedirect,
|
|
} from './mindspace-public-route.mjs';
|
|
|
|
test('resolveMindSpacePublishOwnerKey keeps uuid and resolves usernames', async () => {
|
|
assert.equal(
|
|
await resolveMindSpacePublishOwnerKey('123e4567-e89b-12d3-a456-426614174000'),
|
|
'123e4567-e89b-12d3-a456-426614174000',
|
|
);
|
|
assert.equal(
|
|
await resolveMindSpacePublishOwnerKey('John', async (username) => `${username}-id`),
|
|
'john-id',
|
|
);
|
|
assert.equal(await resolveMindSpacePublishOwnerKey('../bad', async () => 'x'), null);
|
|
});
|
|
|
|
test('public route target resolves inside publish root', () => {
|
|
const result = resolveMindSpacePublicRouteTarget({
|
|
h5Root: '/tmp/h5',
|
|
ownerKey: 'user-1',
|
|
parts: ['public', 'report.html'],
|
|
});
|
|
assert.equal(result.targetDir, path.resolve('/tmp/h5/MindSpace/user-1'));
|
|
assert.equal(result.insideRoot, true);
|
|
assert.equal(result.resolvedPath, path.resolve('/tmp/h5/MindSpace/user-1/public/report.html'));
|
|
});
|
|
|
|
test('canonical redirect helpers normalize route paths', () => {
|
|
assert.equal(buildMindSpaceCanonicalRedirect('user-1', ['public', 'a b.html']), '/MindSpace/user-1/public/a%20b.html');
|
|
assert.equal(resolveMindSpaceSimilarHtmlRedirect({ ownerKey: 'user-1', relativePath: 'public/a b.html' }), '/MindSpace/user-1/public/a%20b.html');
|
|
});
|
|
|
|
test('decodeMindSpaceRouteSegment safely decodes segments', () => {
|
|
assert.equal(decodeMindSpaceRouteSegment('report%201.html'), 'report 1.html');
|
|
assert.equal(decodeMindSpaceRouteSegment('%E4%B8%AD%E6%96%87'), '中文');
|
|
});
|
|
|
|
test('resolveMindSpaceHtmlFallbackRedirect points html to public zone sibling', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ms-public-route-'));
|
|
const targetDir = path.join(root, 'MindSpace', 'user-1');
|
|
const publicDir = path.join(targetDir, 'public');
|
|
fs.mkdirSync(publicDir, { recursive: true });
|
|
fs.writeFileSync(path.join(publicDir, 'report.html'), 'ok');
|
|
const redirect = resolveMindSpaceHtmlFallbackRedirect({
|
|
ownerKey: 'user-1',
|
|
targetDir,
|
|
resolvedRoot: path.resolve(targetDir),
|
|
rest: ['report.html'],
|
|
});
|
|
assert.equal(redirect, '/MindSpace/user-1/public/report.html');
|
|
});
|
|
|
|
test('recoverMisplacedMindSpacePublicHtml copies misplaced file into public zone', async () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ms-public-recover-'));
|
|
const targetDir = path.join(root, 'MindSpace', 'user-1');
|
|
fs.mkdirSync(path.join(root, 'public'), { recursive: true });
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
fs.writeFileSync(path.join(root, 'public', 'report.html'), 'demo', { encoding: 'utf8', flag: 'w' });
|
|
let ensured = null;
|
|
const recovered = await recoverMisplacedMindSpacePublicHtml({
|
|
h5Root: root,
|
|
targetDir,
|
|
resolvedRoot: path.resolve(targetDir),
|
|
rest: ['public', 'report.html'],
|
|
ensureThumbnail: async (...args) => {
|
|
ensured = args;
|
|
},
|
|
logger: { warn() {} },
|
|
});
|
|
assert.equal(recovered, path.join(targetDir, 'public', 'report.html'));
|
|
assert.equal(fs.existsSync(recovered), true);
|
|
assert.deepEqual(ensured, [targetDir, 'public/report.html']);
|
|
});
|
|
|
|
test('resolveMindSpacePublicRequest coordinates canonical redirect, serve, and not found', async () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ms-public-request-'));
|
|
const ownerKey = '123e4567-e89b-12d3-a456-426614174000';
|
|
const targetDir = path.join(root, 'MindSpace', ownerKey);
|
|
fs.mkdirSync(path.join(targetDir, 'public'), { recursive: true });
|
|
fs.writeFileSync(path.join(targetDir, 'public', 'report.html'), 'demo');
|
|
|
|
const redirected = await resolveMindSpacePublicRequest({
|
|
h5Root: root,
|
|
requestPath: '/John/public/report.html',
|
|
resolveUsernameToUserId: async () => ownerKey,
|
|
});
|
|
assert.deepEqual(redirected, {
|
|
action: 'redirect',
|
|
status: 301,
|
|
location: `/MindSpace/${ownerKey}/public/report.html`,
|
|
ownerKey,
|
|
});
|
|
|
|
const served = await resolveMindSpacePublicRequest({
|
|
h5Root: root,
|
|
requestPath: `/${ownerKey}/public/report.html`,
|
|
});
|
|
assert.equal(served.action, 'serve');
|
|
assert.equal(served.filePath, path.join(targetDir, 'public', 'report.html'));
|
|
|
|
const missing = await resolveMindSpacePublicRequest({
|
|
h5Root: root,
|
|
requestPath: `/${ownerKey}/public/missing.html`,
|
|
});
|
|
assert.deepEqual(missing, {
|
|
action: 'not_found',
|
|
reason: 'missing_file',
|
|
ownerKey,
|
|
});
|
|
});
|
|
|
|
test('resolveMindSpacePublicRequest keeps legacy browser-storage pages available during migration', async () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ms-public-storage-ban-'));
|
|
const ownerKey = '123e4567-e89b-12d3-a456-426614174000';
|
|
const targetDir = path.join(root, 'MindSpace', ownerKey, 'public');
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
fs.writeFileSync(
|
|
path.join(targetDir, 'ledger.html'),
|
|
'<script>localStorage.setItem("ledger", "[]")</script>',
|
|
);
|
|
fs.writeFileSync(
|
|
path.join(targetDir, 'ledger.js'),
|
|
'sessionStorage.setItem("ledger", "[]")',
|
|
);
|
|
|
|
const result = await resolveMindSpacePublicRequest({
|
|
h5Root: root,
|
|
requestPath: `/${ownerKey}/public/ledger.html`,
|
|
});
|
|
assert.equal(result.action, 'serve');
|
|
assert.equal(result.filePath, path.join(targetDir, 'ledger.html'));
|
|
|
|
const scriptResult = await resolveMindSpacePublicRequest({
|
|
h5Root: root,
|
|
requestPath: `/${ownerKey}/public/ledger.js`,
|
|
});
|
|
assert.equal(scriptResult.action, 'serve');
|
|
assert.equal(scriptResult.filePath, path.join(targetDir, 'ledger.js'));
|
|
});
|
|
|
|
test('resolveMindSpacePublicRequest serves missing thumbnail png when svg sidecar exists', async () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ms-public-thumb-'));
|
|
const ownerKey = '123e4567-e89b-12d3-a456-426614174000';
|
|
const targetDir = path.join(root, 'MindSpace', ownerKey);
|
|
const publicDir = path.join(targetDir, 'public');
|
|
fs.mkdirSync(publicDir, { recursive: true });
|
|
fs.writeFileSync(path.join(publicDir, 'report.thumbnail.svg'), '<svg xmlns="http://www.w3.org/2000/svg"></svg>');
|
|
|
|
const served = await resolveMindSpacePublicRequest({
|
|
h5Root: root,
|
|
requestPath: `/${ownerKey}/public/report.thumbnail.png`,
|
|
});
|
|
assert.equal(served.action, 'serve');
|
|
assert.equal(served.filePath, path.join(publicDir, 'report.thumbnail.png'));
|
|
assert.equal(fs.existsSync(served.filePath), false);
|
|
|
|
const stillMissing = await resolveMindSpacePublicRequest({
|
|
h5Root: root,
|
|
requestPath: `/${ownerKey}/public/missing.thumbnail.png`,
|
|
});
|
|
assert.deepEqual(stillMissing, {
|
|
action: 'not_found',
|
|
reason: 'missing_file',
|
|
ownerKey,
|
|
});
|
|
});
|