Files
memind/server/portal-workspace-publication-delivery.test.mjs
2026-07-27 15:34:35 +08:00

300 lines
6.6 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
createPortalWorkspacePublicationDelivery,
} from './portal-workspace-publication-delivery.mjs';
function createResponse() {
return {
statusCode: 200,
body: undefined,
headers: {},
redirectValue: null,
status(code) {
this.statusCode = code;
return this;
},
type(value) {
this.headers['Content-Type'] = value;
return this;
},
set(name, value) {
this.headers[name] = value;
return this;
},
json(body) {
this.body = body;
return this;
},
send(body) {
this.body = body;
return this;
},
redirect(status, location) {
this.statusCode = status;
this.redirectValue = location;
return this;
},
};
}
function createRequest(overrides = {}) {
return {
path: '/user-1/public/page.html',
originalUrl:
'/MindSpace/user-1/public/page.html',
url: '/user-1/public/page.html',
query: {},
userSession: null,
userToken: null,
get(name) {
return name === 'user-agent'
? 'test-agent'
: '';
},
...overrides,
};
}
function htmlDelivery() {
return {
action: 'deliver',
kind: 'html',
ownerId: 'user-1',
relativePath: 'public/page.html',
servedName: 'page.html',
canonicalPath:
'/MindSpace/user-1/public/page.html',
thumbnailName: 'page.thumbnail.png',
html:
'<!doctype html><html><head><title>Demo</title></head><body><main>Demo</main></body></html>',
};
}
function createSetup(overrides = {}) {
let deliveryResult = htmlDelivery();
let userAuth = null;
const calls = [];
const deliveryService = {
async resolveWorkspaceRequest(input) {
calls.push([
'resolve-workspace',
input,
]);
return deliveryResult;
},
async renderLongImage(input) {
calls.push(['long-image', input]);
return {
servedName:
'mindspace-public-page.long.png',
bodyBase64:
Buffer.from('png-body').toString(
'base64',
),
};
},
};
const service =
createPortalWorkspacePublicationDelivery({
internalAgentSecret: 'test-secret',
analyticsConfig: { enabled: false },
rybbitConfig: { enabled: false },
getAuthPool: () => null,
getUserAuth: () => userAuth,
getMindSpacePages: () => null,
getWorkspacePublicationDelivery: () =>
deliveryService,
resolveRequestOrigin: () =>
'https://portal.example',
removeQueryParam(url, key) {
calls.push([
'remove-query',
url,
key,
]);
return new URL(url)
.toString()
.replace(
new RegExp(
`([?&])${key}=[^&]*&?`,
),
'$1',
)
.replace(/[?&]$/, '');
},
...overrides,
});
return {
service,
calls,
deliveryService,
setDeliveryResult(value) {
deliveryResult = value;
},
setUserAuth(value) {
userAuth = value;
},
};
}
async function serve(setup, request) {
const response = createResponse();
await setup.service.serveUserPublishFile(
request ?? createRequest(),
response,
() => {},
);
return response;
}
test('maps redirects, forbidden, not-ready, and not-found delivery results', async () => {
const setup = createSetup();
setup.setDeliveryResult({
action: 'redirect',
status: 302,
location: '/canonical',
});
const redirect = await serve(setup);
assert.equal(redirect.statusCode, 302);
assert.equal(
redirect.redirectValue,
'/canonical',
);
setup.setDeliveryResult({
action: 'forbidden',
});
const forbidden = await serve(setup);
assert.equal(forbidden.statusCode, 403);
setup.setDeliveryResult({
action: 'not_ready',
});
const notReady = await serve(setup);
assert.equal(notReady.statusCode, 409);
assert.match(
notReady.body,
/正在完成发布验证/,
);
for (const [reason, message] of [
['missing_owner_dir', '用户不存在'],
[
'missing_directory_index',
'目录中没有 index.html',
],
['missing_file', '文件不存在'],
]) {
setup.setDeliveryResult({
action: 'not_found',
reason,
});
const response = await serve(setup);
assert.equal(response.statusCode, 404);
assert.deepEqual(response.body, {
message,
});
}
});
test('passes only the logical request path to MindSpace', async () => {
const setup = createSetup();
await serve(setup);
assert.deepEqual(setup.calls[0], [
'resolve-workspace',
{
requestPath:
'/user-1/public/page.html',
},
]);
});
test('proxies binary bodies without receiving a physical path', async () => {
const setup = createSetup();
setup.setDeliveryResult({
action: 'deliver',
kind: 'binary',
ownerId: 'user-1',
relativePath:
'public/page.thumbnail.png',
servedName: 'page.thumbnail.png',
bodyBase64:
Buffer.from('png-body').toString(
'base64',
),
cacheControl: 'public, max-age=300',
});
const response = await serve(setup);
assert.equal(
response.body.toString(),
'png-body',
);
assert.equal(
response.headers['Cache-Control'],
'public, max-age=300',
);
assert.equal(
Object.hasOwn(
setup.calls[0][1],
'filePath',
),
false,
);
});
test('decorates HTML at the edge and preserves viewer-specific no-store delivery', async () => {
const setup = createSetup();
setup.setUserAuth({
async getMe() {
return { id: 'viewer-1' };
},
async getUserById() {
return { id: 'user-1' };
},
});
const response = await serve(setup);
assert.equal(response.statusCode, 200);
assert.equal(
response.headers['Cache-Control'],
'private, no-store',
);
assert.equal(
response.headers['Content-Type'],
'text/html; charset=utf-8',
);
assert.match(String(response.body), /Demo/);
assert.match(
String(response.body),
/page\.thumbnail\.png/,
);
});
test('delegates long-image rendering to MindSpace', async () => {
const setup = createSetup();
const response = await serve(
setup,
createRequest({
originalUrl:
'/MindSpace/user-1/public/page.html?download=long-image',
query: {
download: 'long-image',
},
}),
);
assert.equal(
response.headers['Content-Type'],
'image/png',
);
assert.equal(
response.body.toString(),
'png-body',
);
assert.equal(
setup.calls.some(
([kind]) => kind === 'long-image',
),
true,
);
});