test: restore mnote-web lib baseline
This commit is contained in:
+26
-3
@@ -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
|
||||
```
|
||||
@@ -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"));
|
||||
|
||||
Reference in New Issue
Block a user