Files
mnote/scripts/task115-rust-web-document-shell-smoke.js
T

169 lines
6.0 KiB
JavaScript
Raw Normal View History

2026-04-29 12:24:44 +08:00
#!/usr/bin/env node
"use strict";
const assert = require("node:assert");
const {
fetchWithTimeout,
findFreePort,
startGateway,
waitForGateway,
} = require("./task114-rust-web-gateway-entry-smoke.js");
const EXTERNAL_BASE_URL = (process.env.MNOTE_UI_BASE_URL || "").replace(/\/+$/, "");
const CONFIGURED_DOCUMENT_ID = (process.env.MNOTE_DOCUMENT_ID || "").trim();
const CONFIGURED_WORKSPACE_ID = (process.env.MNOTE_WORKSPACE_ID || "").trim();
const CONFIGURED_TITLE = (process.env.MNOTE_DOCUMENT_TITLE || "").trim();
async function readJsonResponse(response, label) {
const text = await response.text();
let payload = null;
try {
payload = text ? JSON.parse(text) : null;
} catch {
throw new Error(`${label} 返回了非 JSON 内容: ${text.slice(0, 1200)}`);
}
assert(response.ok, `${label} 请求失败: ${response.status}; body: ${text.slice(0, 1200)}`);
return payload;
}
async function createTempDocument(baseUrl) {
const title = `task115-rust-web-${Date.now()}`;
const requestBody = {
action: "create",
title,
};
if (CONFIGURED_WORKSPACE_ID) {
requestBody.workspaceId = CONFIGURED_WORKSPACE_ID;
}
const response = await fetchWithTimeout(`${baseUrl}/api/tree/commands`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(requestBody),
});
const payload = await readJsonResponse(response, "创建临时文档");
const result = payload && typeof payload.result === "object" ? payload.result : null;
const documentId = result && typeof result.documentId === "string" ? result.documentId : "";
const workspaceId = result && typeof result.workspaceId === "string" ? result.workspaceId : "";
assert(documentId, "创建临时文档缺少 documentId");
assert(workspaceId, "创建临时文档缺少 workspaceId");
return { documentId, workspaceId, title };
}
async function purgeTempDocument(baseUrl, target) {
const response = await fetchWithTimeout(`${baseUrl}/api/tree/commands`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
action: "purge",
workspaceId: target.workspaceId,
documentId: target.documentId,
}),
});
await readJsonResponse(response, "清理临时文档");
}
async function validateDocumentShell(baseUrl, target) {
const targetPath = `/documents/${encodeURIComponent(target.documentId)}?workspaceId=${encodeURIComponent(target.workspaceId)}`;
const response = await fetchWithTimeout(`${baseUrl}${targetPath}`);
const html = await response.text();
assert.equal(response.status, 200, `文档 shell 请求失败: ${response.status}; body: ${html.slice(0, 1200)}`);
assert.equal(response.headers.get("x-mnote-web-owner"), "mnote-web");
assert.equal(response.headers.get("x-mnote-web-shell"), "document");
assert.match(response.headers.get("content-type") || "", /text\/html/);
assert.match(html, /data-mnote-shell="document"/);
assert.match(html, /data-testid="wolai-sidebar"/);
assert.match(html, /data-testid="wolai-topbar"/);
assert.match(html, /data-testid="wolai-floating-ai"/);
assert.match(html, /data-page-aggregate-snapshot="mnote\.page_aggregate\.v1"/);
assert.match(html, /id="__MNOTE_PAGE_AGGREGATE__"/);
assert.match(html, /data-editor-host="leptos_tiptap_island"/);
assert.doesNotMatch(html, /mnote-web-document-shell/);
assert.doesNotMatch(html, /convex_upstream_error|未登录/);
const aggregate = await fetchWithTimeout(
`${baseUrl}/api/page-aggregate/${encodeURIComponent(target.documentId)}?workspaceId=${encodeURIComponent(target.workspaceId)}`,
);
const aggregateText = await aggregate.text();
assert.equal(aggregate.status, 200, `page aggregate 请求失败: ${aggregate.status}; body: ${aggregateText.slice(0, 1200)}`);
const payload = JSON.parse(aggregateText);
assert.equal(aggregate.headers.get("x-mnote-web-owner"), "mnote-web");
assert.equal(payload.owner, "mnote-web");
assert.equal(payload.schema, "mnote.page_aggregate.v1");
assert.equal(payload.result.identity.documentId, target.documentId);
assert.equal(payload.result.identity.workspaceId, target.workspaceId);
assert.equal(payload.result.schema, "mnote.page_aggregate.v1");
assert.equal(typeof payload.result.head.title, "string");
assert(payload.result.head.title.trim(), "Page Aggregate 标题不能为空");
if (target.expectedTitle) {
assert.equal(payload.result.head.title, target.expectedTitle);
}
}
async function main() {
let gateway = null;
let baseUrl = EXTERNAL_BASE_URL;
let createdTarget = null;
if (!baseUrl) {
const port = await findFreePort();
baseUrl = `http://127.0.0.1:${port}`;
gateway = startGateway(port);
await waitForGateway(baseUrl);
}
try {
const target = CONFIGURED_DOCUMENT_ID
? {
documentId: CONFIGURED_DOCUMENT_ID,
workspaceId: CONFIGURED_WORKSPACE_ID || "ws_demo",
expectedTitle: CONFIGURED_TITLE || null,
}
: gateway
? {
documentId: "doc_1",
workspaceId: "ws_demo",
expectedTitle: CONFIGURED_TITLE || "服务端页面",
}
: await createTempDocument(baseUrl);
if (!CONFIGURED_DOCUMENT_ID && !gateway) {
createdTarget = target;
target.expectedTitle = target.title;
}
await validateDocumentShell(baseUrl, target);
console.log(
JSON.stringify(
{
ok: true,
baseUrl,
owner: "mnote-web",
shell: "document",
editorHost: "leptos_tiptap_island",
documentId: target.documentId,
workspaceId: target.workspaceId,
},
null,
2,
),
);
} finally {
if (createdTarget) {
await purgeTempDocument(baseUrl, createdTarget);
}
if (gateway && gateway.pid) {
gateway.kill("SIGTERM");
setTimeout(() => {
if (!gateway.killed) {
gateway.kill("SIGKILL");
}
}, 2000).unref();
}
}
}
main().catch((error) => {
console.error(error instanceof Error ? error.stack || error.message : String(error));
process.exit(1);
});