300 lines
6.6 KiB
JavaScript
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,
|
|
);
|
|
});
|