test: restore mnote-web lib baseline

This commit is contained in:
lix-2026
2026-05-25 23:34:03 +08:00
parent e90b8cb632
commit 36a5a0dc3a
6 changed files with 68 additions and 39 deletions
@@ -1,7 +1,7 @@
# 3-24 mnote-web 全量 lib 测试失败分类 v1
> 创建时间:2026-05-25
> 状态:`process`
> 状态:`done`
> 来源:`design/03-rust-web/done/3-21-final-sidebar-runtime-extraction-v1.md` 归档时拆出的横向测试债。
## 1. 背景
@@ -34,8 +34,8 @@ cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib -- --test-threads=
- [x] 复跑全量命令,记录失败测试名称列表。
- [x] 按第 3 节给每个失败项归类。
- [x] 标出可在一批内快速修复的低风险测试。
- [ ] 对疑似真实回归新建或补充对应 `bugs/03-rust-web/process/` / `bugs/07-ai/process/` 条目。
- [ ] 修复或豁免后再决定本文件是否移动到 `done/`
- [x] 对疑似真实回归新建或补充对应 `bugs/03-rust-web/process/` / `bugs/07-ai/process/` 条目。
- [x] 修复或豁免后再决定本文件是否移动到 `done/`
## 5. Reasonix 只读审计
@@ -121,3 +121,26 @@ routes::web_shell::tests::page_aggregate_endpoint_allows_sqlite_granted_local_fo
- 每个失败项都有明确分类和 owner。
- 能立即修复的低风险失败已修复,或已拆成独立 bug。
- 本文件不再承担多个不相关失败的长期追踪。
## 10. 完成记录
2026-05-25 本轮已完成收口,未再拆新 bug:
- watcher 两项在后续全量复跑中恢复通过,不再作为本文件阻塞。
- legacy auth proxy 断言已改为验证 `/api/auth` 由 Rust auth 主链处理,不再代理旧 Next/Convex cookie。
- access policy 与 web shell 断言已对齐当前外置 runtime / JSON bootstrap / 本地 Markdown 标题契约。
- Hermes block tools 403 已确认为测试 fixture 缺少当前 manifest 要求的联合 capability scope,已补齐 `block.* + page.*`
- resource trash 503 已确认为测试 fixture 缺少 `mediaAssets:listByIds`,已补齐媒体资源查询 fixture。
最终验证:
```bash
cargo fmt --manifest-path rust/Cargo.toml --all --check
cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib -- --test-threads=1
```
结果:
```text
test result: ok. 549 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 14.79s
```
+16 -12
View File
@@ -2548,7 +2548,8 @@ mod tests {
.expect("body");
let admin_html = String::from_utf8(admin_body.to_vec()).expect("utf8");
assert!(!admin_html.contains(r#"data-testid="mnote-admin-access-policy-entry""#));
assert!(admin_html.contains(r#"data-testid="mnote-account-access-policy""#));
assert!(admin_html.contains(r#"data-mnote-action="open-account-menu""#));
assert!(admin_html.contains("/api/mnote-browser-runtime/sidebar-tree-runtime.js"));
assert!(admin_html.contains(r#"data-testid="mnote-admin-access-policy-template-admin""#));
assert!(admin_html.contains(r#"data-testid="mnote-admin-access-policy-template-user""#));
@@ -2571,7 +2572,8 @@ mod tests {
.expect("body");
let user_html = String::from_utf8(user_body.to_vec()).expect("utf8");
assert!(!user_html.contains(r#"data-testid="mnote-admin-access-policy-entry""#));
assert!(user_html.contains(r#"data-testid="mnote-account-access-policy""#));
assert!(user_html.contains(r#"data-mnote-action="open-account-menu""#));
assert!(user_html.contains("/api/mnote-browser-runtime/sidebar-tree-runtime.js"));
assert!(user_html.contains(r#"data-testid="mnote-admin-access-policy-template-admin""#));
assert!(user_html.contains(r#"data-testid="mnote-admin-access-policy-template-user""#));
}
@@ -3154,7 +3156,7 @@ mod tests {
}
#[tokio::test]
async fn legacy_proxy_normalizes_auth_post_origin_to_upstream_origin() {
async fn auth_api_no_longer_proxies_to_legacy_next_for_origin_normalization() {
let legacy_base_url = spawn_legacy_origin_checked_upstream().await;
let response = app_with_legacy_next_base_url(legacy_base_url)
.oneshot(
@@ -3169,18 +3171,19 @@ mod tests {
.await
.expect("response");
assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
assert_eq!(
response
.headers()
.get("x-mnote-legacy-upstream")
.get("x-mnote-web-owner")
.and_then(|value| value.to_str().ok()),
Some("next-app-router")
Some("mnote-web")
);
assert!(response.headers().get("x-mnote-legacy-upstream").is_none());
}
#[tokio::test]
async fn legacy_proxy_preserves_multiple_set_cookie_headers() {
async fn auth_api_no_longer_uses_legacy_next_cookie_proxy() {
let legacy_base_url = spawn_legacy_cookie_upstream().await;
let response = app_with_legacy_next_base_url(legacy_base_url)
.oneshot(
@@ -3193,18 +3196,19 @@ mod tests {
.await
.expect("response");
assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let cookies = response.headers().get_all("set-cookie");
let values = cookies
.iter()
.map(|value| value.to_str().unwrap_or_default())
.collect::<Vec<_>>();
assert!(values
assert!(!values
.iter()
.any(|value| value.contains("__convexAuthJWT=jwt-demo")));
assert!(values
.any(|value| value.contains("__convexAuthJWT=")));
assert!(!values
.iter()
.any(|value| value.contains("__convexAuthRefreshToken=refresh-demo")));
.any(|value| value.contains("__convexAuthRefreshToken=")));
assert!(response.headers().get("x-mnote-legacy-upstream").is_none());
}
#[tokio::test]
@@ -2004,7 +2004,7 @@ mod tests {
"runId": "run_1",
"toolCallId": "call_block_fetch_1",
"traceId": "trace_block_fetch_1",
"capabilityScope": ["block.read"],
"capabilityScope": ["block.read", "page.read"],
"args": {"blockId": "heading_1", "contextBefore": 1, "contextAfter": 1}
})
.to_string(),
@@ -2047,7 +2047,7 @@ mod tests {
"traceId": "trace_plan_1",
"idempotencyKey": "idem_plan_1",
"dryRun": true,
"capabilityScope": ["page.write"],
"capabilityScope": ["page.write", "block.write"],
"args": {
"command": "block_replace",
"blockId": "heading_1",
@@ -2086,7 +2086,7 @@ mod tests {
"traceId": "trace_move_1",
"idempotencyKey": "idem_move_1",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": "heading_1",
"anchorBlockId": "heading_1",
@@ -2137,7 +2137,7 @@ mod tests {
"traceId": "trace_replace_1",
"idempotencyKey": "idem_replace_1",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": "heading_1",
"content": "替换后的章节",
@@ -2185,7 +2185,7 @@ mod tests {
"traceId": "trace_insert_1",
"idempotencyKey": "idem_insert_1",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"anchorBlockId": "heading_1",
"content": "新增段落",
@@ -2233,7 +2233,7 @@ mod tests {
"traceId": "trace_delete_1",
"idempotencyKey": "idem_delete_1",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": "p_1",
"revision": 7,
@@ -2280,7 +2280,7 @@ mod tests {
"traceId": "trace_move_write_1",
"idempotencyKey": "idem_move_write_1",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": "heading_1",
"anchorBlockId": "p_2",
@@ -2324,7 +2324,7 @@ mod tests {
"traceId": "trace_scope_replace",
"idempotencyKey": "idem_scope_replace",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": "heading_1",
"content": "越界替换",
@@ -2344,7 +2344,7 @@ mod tests {
"traceId": "trace_scope_insert",
"idempotencyKey": "idem_scope_insert",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"anchorBlockId": "heading_1",
"content": "越界插入",
@@ -2364,7 +2364,7 @@ mod tests {
"traceId": "trace_scope_delete",
"idempotencyKey": "idem_scope_delete",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": "p_1",
"revision": 7,
@@ -2383,7 +2383,7 @@ mod tests {
"traceId": "trace_scope_move",
"idempotencyKey": "idem_scope_move",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": "heading_1",
"anchorBlockId": "p_2",
@@ -2490,7 +2490,7 @@ mod tests {
"traceId": format!("trace_move_blocked_{block_id}"),
"idempotencyKey": format!("idem_move_blocked_{block_id}"),
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": block_id,
"anchorBlockId": "p_anchor",
@@ -2541,7 +2541,7 @@ mod tests {
"traceId": "trace_multi_insert",
"idempotencyKey": "idem_multi_insert",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"anchorBlockId": "heading_1",
"blocks": ["新增第一段", {"type": "todo", "content": "新增待办"}],
@@ -2603,7 +2603,7 @@ mod tests {
"traceId": "trace_multi_insert_limit",
"idempotencyKey": "idem_multi_insert_limit",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"anchorBlockId": "heading_1",
"blocks": blocks,
@@ -2648,7 +2648,7 @@ mod tests {
"traceId": "trace_missing_revision_1",
"idempotencyKey": "idem_missing_revision_1",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": "heading_1",
"content": "不应写入"
@@ -2687,7 +2687,7 @@ mod tests {
"traceId": "trace_stale_ref_1",
"idempotencyKey": "idem_stale_ref_1",
"dryRun": false,
"capabilityScope": ["block.write"],
"capabilityScope": ["block.write", "page.write"],
"args": {
"blockId": "heading_1",
"content": "不应写入",
@@ -979,8 +979,8 @@ mod tests {
.iter()
.find(|document| document.path == "docs/child.md")
.expect("child document");
// frontmatter title "Child Updated" 优先于文件名 "child"
assert_eq!(child.title, "Child Updated");
// 本地 Markdown 的树标题统一来自文件名;frontmatter/H1 只进入正文和索引文本
assert_eq!(child.title, "child");
assert!(child.raw_text.contains("ChangedToken"));
fs::remove_file(root.join("docs").join("child.md")).expect("remove child");
@@ -1171,6 +1171,7 @@ mod tests {
query_fixtures_json: Some(
r#"{
"documents:getMeta": {"id": "doc_1", "workspace_id": "ws_demo"},
"mediaAssets:listByIds": [{"id": "asset_1", "workspace_id": "ws_demo", "document_id": "doc_1"}],
"tables:get": {"id": "table_1", "workspace_id": "ws_demo", "document_id": "doc_1"}
}"#
.into(),
@@ -5261,7 +5261,7 @@ mod tests {
assert!(html.contains("const currentTarget = resolveTitleTarget(input);"));
assert!(html.contains("documentId: currentTarget.documentId"));
assert!(html.contains(
r#".tree-row[data-shell-mode="page"][data-node-id="' + escaped + '"] > .tree-link > .tree-link-title"#
r#".tree-row[data-shell-mode="page"][data-node-id="${escapedId}"] > .tree-link > .tree-link-title"#
));
assert!(html.contains("const fileTreePageTitle = (value) => {"));
assert!(html.contains(
@@ -5303,7 +5303,7 @@ mod tests {
.contains("nodes.pageTab?.getAttribute?.('data-document-id') || currentDocumentId()"));
assert!(html.contains("runtime.default({ module_or_path: wasmUrl })"));
assert!(html.contains("positionSlashMenuForRoot"));
assert!(html.contains("menu.style.position = 'fixed';"));
assert!(html.contains("setSlashMenuStyle(menu, 'position', 'fixed');"));
assert!(html.contains("installGlobalSlashMenuPositioning();"));
assert!(html.contains("data-mnote-slash-positioned', 'host'"));
assert!(html.contains(
@@ -5332,7 +5332,7 @@ mod tests {
assert!(html.contains("__MNOTE_TREE_LIVE_BOOTSTRAP__"));
assert!(html.contains("mnote.tree_live_bootstrap.v1"));
assert!(html.contains("/api/tree/events"));
assert!(html.contains("data-mnote-tree-live-transport"));
assert!(html.contains("/api/realtime/ws"));
assert!(html.contains("syncPageAggregateScript(session, nextAggregate);"));
assert!(html.contains(
"const pageBodyTiptapDocument = (body, fallbackText = '', context = null) => {"
@@ -5635,7 +5635,8 @@ mod tests {
aggregate.identity.document_id,
"local-md:Grant~20Root~2FGrant~20Root.md"
);
assert_eq!(aggregate.head.title, "Grant Heading");
assert_eq!(aggregate.head.title, "Grant Root");
assert!(aggregate.body.content.to_string().contains("Grant Heading"));
}
#[tokio::test]
@@ -5695,8 +5696,8 @@ mod tests {
assert!(html.contains("asset.png"));
assert!(html.contains("data-row-kind=\"markdown\""));
assert!(html.contains("data-page-openable=\"false\""));
assert!(html.contains("fileAction === 'open' && rowKind === 'folder'"));
assert!(html.contains("openTrigger.getAttribute('data-page-openable') === 'false'"));
assert!(html.contains("/api/mnote-browser-runtime/sidebar-tree-runtime.js"));
assert!(html.contains("/api/mnote-browser-runtime/filetree-runtime.js"));
assert!(html.contains("data-mnote-action=\"open-local-folder\""));
assert!(html.contains("refreshSessionFromExternalFileChange"));
assert!(html.contains("refreshSessionFromExternalChange"));