chore: 保存当前架构收口与 bug 修复快照
归档本轮 P0/P1 bug 修复、设计审查迁移、AI selection scope 收口与 stream contract 调整,并保留当前 05 主线迁移起点。
This commit is contained in:
@@ -16,6 +16,15 @@ const {
|
||||
} = require("./tree-shell-smoke-helpers");
|
||||
|
||||
const OUT_DIR = path.join(process.cwd(), "tmp", "page-aggregate-refresh-persistence-smoke");
|
||||
const CHROME_EXECUTABLE = process.env.PLAYWRIGHT_CHROME_EXECUTABLE || "";
|
||||
|
||||
async function callMnoteTool(requestContext, payload) {
|
||||
return requestJson(requestContext, "/api/hermes/tools/mnote/call", {
|
||||
method: "POST",
|
||||
headers: { "x-mnote-actor-id": payload.actorId || "smoke-user" },
|
||||
data: payload,
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchPageAggregate(requestContext, workspaceId, documentId) {
|
||||
const payload = await requestJson(
|
||||
@@ -27,6 +36,37 @@ async function fetchPageAggregate(requestContext, workspaceId, documentId) {
|
||||
return payload.result;
|
||||
}
|
||||
|
||||
function pageSubtreeSummary(aggregate) {
|
||||
const subtree = aggregate?.tree?.pageSubtree ?? null;
|
||||
return {
|
||||
rootNodeId: subtree?.rootNodeId ?? null,
|
||||
outlineLength: Array.isArray(subtree?.outline) ? subtree.outline.length : null,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchAiDoc(requestContext, target, actorId, suffix, label) {
|
||||
const response = await callMnoteTool(requestContext, {
|
||||
toolName: "mnote.doc.fetch",
|
||||
workspaceId: target.workspaceId,
|
||||
documentId: target.documentId,
|
||||
actorId,
|
||||
sessionId: `sess_page_aggregate_ai_fetch_${suffix}`,
|
||||
runId: `run_page_aggregate_ai_fetch_${suffix}_${label}`,
|
||||
toolCallId: `call_page_aggregate_ai_fetch_${suffix}_${label}`,
|
||||
traceId: `trace_page_aggregate_ai_fetch_${suffix}_${label}`,
|
||||
capabilityScope: ["page.read"],
|
||||
args: {
|
||||
scope: "full",
|
||||
detail: "with_ids",
|
||||
maxBlocks: 20,
|
||||
},
|
||||
});
|
||||
assert.equal(response.ok, true, `${label}: mnote.doc.fetch 应成功`);
|
||||
assert.equal(response.result.schema, "mnote.page_ai_context.v1", `${label}: AI fetch schema 应稳定`);
|
||||
assert(Array.isArray(response.result.blocks), `${label}: AI fetch 应返回 blocks`);
|
||||
return response.result;
|
||||
}
|
||||
|
||||
function findBlockWithText(aggregate, text) {
|
||||
const blocks = aggregate?.body?.blockDocument?.blocks;
|
||||
if (!Array.isArray(blocks)) return null;
|
||||
@@ -289,7 +329,11 @@ async function main() {
|
||||
const bodyText = `Page Aggregate refresh body ${suffix}`;
|
||||
const evidencePath = path.join(OUT_DIR, `${suffix}.json`);
|
||||
const screenshotPath = path.join(OUT_DIR, `${suffix}.png`);
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const browser = await chromium.launch({
|
||||
headless: true,
|
||||
...(CHROME_EXECUTABLE ? { executablePath: CHROME_EXECUTABLE } : {}),
|
||||
args: ["--no-sandbox", "--disable-dev-shm-usage"],
|
||||
});
|
||||
const context = await browser.newContext({ viewport: { width: 1440, height: 960 } });
|
||||
const page = await context.newPage();
|
||||
const createdIds = [];
|
||||
@@ -325,6 +369,20 @@ async function main() {
|
||||
title,
|
||||
bodyText,
|
||||
);
|
||||
const beforeReloadAiFetch = await fetchAiDoc(context.request, target, viewer.userId, suffix, "before_reload");
|
||||
const beforeReloadAiBlock = beforeReloadAiFetch.blocks.find((block) => block.blockId === beforeReloadBlock.blockId);
|
||||
assert.equal(beforeReloadAiFetch.revision, beforeReloadAggregate.body?.revision, "刷新前 AI fetch revision 应与 Page Aggregate 一致");
|
||||
assert.equal(
|
||||
beforeReloadAiFetch.conflictDetectionKey,
|
||||
beforeReloadAggregate.body?.conflictDetectionKey,
|
||||
"刷新前 AI fetch conflictDetectionKey 应与 Page Aggregate 一致",
|
||||
);
|
||||
assert.equal(beforeReloadAiBlock?.text, bodyText, "刷新前 AI fetch 应回读 Page Aggregate 正文块");
|
||||
assert.equal(
|
||||
beforeReloadAggregate.tree?.pageSubtree?.rootNodeId,
|
||||
target.documentId,
|
||||
"刷新前 Page Aggregate pageSubtree.rootNodeId 应等于 documentId",
|
||||
);
|
||||
|
||||
await page.reload({ waitUntil: "commit", timeout: UI_TIMEOUT_MS });
|
||||
await waitForPageTitleInput(page);
|
||||
@@ -354,6 +412,20 @@ async function main() {
|
||||
title,
|
||||
bodyText,
|
||||
);
|
||||
const afterReloadAiFetch = await fetchAiDoc(context.request, target, viewer.userId, suffix, "after_reload");
|
||||
const afterReloadAiBlock = afterReloadAiFetch.blocks.find((block) => block.blockId === afterReloadBlock.blockId);
|
||||
assert.equal(afterReloadAiFetch.revision, afterReloadAggregate.body?.revision, "刷新后 AI fetch revision 应与 Page Aggregate 一致");
|
||||
assert.equal(
|
||||
afterReloadAiFetch.conflictDetectionKey,
|
||||
afterReloadAggregate.body?.conflictDetectionKey,
|
||||
"刷新后 AI fetch conflictDetectionKey 应与 Page Aggregate 一致",
|
||||
);
|
||||
assert.equal(afterReloadAiBlock?.text, bodyText, "刷新后 AI fetch 应回读 Page Aggregate 正文块");
|
||||
assert.equal(
|
||||
afterReloadAggregate.tree?.pageSubtree?.rootNodeId,
|
||||
target.documentId,
|
||||
"刷新后 Page Aggregate pageSubtree.rootNodeId 应等于 documentId",
|
||||
);
|
||||
|
||||
await page.screenshot({ path: screenshotPath, fullPage: false });
|
||||
const evidence = {
|
||||
@@ -383,12 +455,23 @@ async function main() {
|
||||
conflictDetectionKey: beforeReloadAggregate.body?.conflictDetectionKey ?? null,
|
||||
pageOptions: beforeReloadAggregate.layout?.pageOptions ?? null,
|
||||
blockProjectionVersion: beforeReloadAggregate.body?.blockProjectionVersion ?? null,
|
||||
pageSubtree: pageSubtreeSummary(beforeReloadAggregate),
|
||||
},
|
||||
matchedBlock: {
|
||||
blockId: beforeReloadBlock.blockId,
|
||||
text: beforeReloadBlock.text,
|
||||
revisionRef: beforeReloadBlock.revisionRef,
|
||||
},
|
||||
aiFetch: {
|
||||
schema: beforeReloadAiFetch.schema,
|
||||
revision: beforeReloadAiFetch.revision,
|
||||
conflictDetectionKey: beforeReloadAiFetch.conflictDetectionKey,
|
||||
matchedBlock: {
|
||||
blockId: beforeReloadAiBlock?.blockId ?? null,
|
||||
text: beforeReloadAiBlock?.text ?? null,
|
||||
revisionRef: beforeReloadAiBlock?.revisionRef ?? null,
|
||||
},
|
||||
},
|
||||
runtime: beforeReloadRuntime,
|
||||
},
|
||||
afterReload: {
|
||||
@@ -398,12 +481,23 @@ async function main() {
|
||||
conflictDetectionKey: afterReloadAggregate.body?.conflictDetectionKey ?? null,
|
||||
pageOptions: afterReloadAggregate.layout?.pageOptions ?? null,
|
||||
blockProjectionVersion: afterReloadAggregate.body?.blockProjectionVersion ?? null,
|
||||
pageSubtree: pageSubtreeSummary(afterReloadAggregate),
|
||||
},
|
||||
matchedBlock: {
|
||||
blockId: afterReloadBlock.blockId,
|
||||
text: afterReloadBlock.text,
|
||||
revisionRef: afterReloadBlock.revisionRef,
|
||||
},
|
||||
aiFetch: {
|
||||
schema: afterReloadAiFetch.schema,
|
||||
revision: afterReloadAiFetch.revision,
|
||||
conflictDetectionKey: afterReloadAiFetch.conflictDetectionKey,
|
||||
matchedBlock: {
|
||||
blockId: afterReloadAiBlock?.blockId ?? null,
|
||||
text: afterReloadAiBlock?.text ?? null,
|
||||
revisionRef: afterReloadAiBlock?.revisionRef ?? null,
|
||||
},
|
||||
},
|
||||
title: afterReloadTitle,
|
||||
editorText: afterReloadEditorText,
|
||||
runtime: afterReloadRuntime,
|
||||
|
||||
Reference in New Issue
Block a user