chore: 收口 review 执行清单与 runtime 验证
- 补齐 design/10-review 执行清单、验收标准与相关设计治理记录 - 迁移已完成的 tree、mindmap、runtime fallback、AI kernel 等设计和缺陷条目 - 推进 Rust Web runtime、tree/sidebar、page aggregate、mindmap 与 OnlyOffice 路由侧验证支撑 - 增加 task177-task180 smoke/audit 脚本及前端相关测试覆盖
This commit is contained in:
@@ -2437,10 +2437,9 @@ pub(crate) async fn load_workspace_shell_projection(
|
||||
query: None,
|
||||
max_results: None,
|
||||
};
|
||||
let dataset = load_projection_snapshot(config, context, &spec)
|
||||
.await
|
||||
.map(|snapshot| snapshot.dataset)
|
||||
.unwrap_or_else(|_| {
|
||||
let dataset = match load_projection_snapshot(config, context, &spec).await {
|
||||
Ok(snapshot) => snapshot.dataset,
|
||||
Err(_) if config.allow_dev_fixtures => {
|
||||
let documents = active_document_id
|
||||
.map(|document_id| {
|
||||
json!([{
|
||||
@@ -2457,9 +2456,19 @@ pub(crate) async fn load_workspace_shell_projection(
|
||||
"active_workspace_id": workspace_id,
|
||||
"active_page_id": active_document_id,
|
||||
"workspaces": [{ "id": workspace_id, "name": default_workspace_name }],
|
||||
"documents": documents
|
||||
"documents": documents,
|
||||
"dev_fixture": true
|
||||
})
|
||||
});
|
||||
}
|
||||
Err(_) => json!({
|
||||
"active_workspace_id": workspace_id,
|
||||
"active_page_id": active_document_id,
|
||||
"workspaces": [{ "id": workspace_id, "name": default_workspace_name }],
|
||||
"documents": [],
|
||||
"degraded": true,
|
||||
"degraded_reason": "projection_unavailable"
|
||||
}),
|
||||
};
|
||||
|
||||
build_workspace_shell_projection(
|
||||
&dataset,
|
||||
@@ -2489,7 +2498,7 @@ pub(crate) async fn load_sidebar_tree_html(
|
||||
max_results: None,
|
||||
};
|
||||
let result = match load_projection_snapshot(config, context, &spec).await {
|
||||
Ok(snapshot) => Some(snapshot.projection),
|
||||
Ok(snapshot) => Some((snapshot.projection, false)),
|
||||
Err(_) if config.allow_dev_fixtures => {
|
||||
// Dev 模式降级:如果调用方已经有 active 页面,优先保留这条真实选择链。
|
||||
let documents = active_document_id
|
||||
@@ -2518,17 +2527,20 @@ pub(crate) async fn load_sidebar_tree_html(
|
||||
"mindmap_docs": [],
|
||||
"mindmap_asset_children": {}
|
||||
});
|
||||
execute_kernel_query(context, workspace_id, projection_query(&spec), dev_dataset).ok()
|
||||
execute_kernel_query(context, workspace_id, projection_query(&spec), dev_dataset)
|
||||
.ok()
|
||||
.map(|projection| (projection, true))
|
||||
}
|
||||
Err(_) => None,
|
||||
};
|
||||
result.map(|projection| {
|
||||
result.map(|(projection, dev_fixture)| {
|
||||
let rows = collect_page_tree_render_rows(&projection);
|
||||
render_initial_page_tree_html(&PageTreeInitialRenderInput {
|
||||
let html = render_initial_page_tree_html(&PageTreeInitialRenderInput {
|
||||
rows,
|
||||
active_node_id: active_document_id.map(ToOwned::to_owned),
|
||||
focused_node_id: None,
|
||||
})
|
||||
});
|
||||
mark_dev_fixture_html(html, dev_fixture, "sidebar-tree")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2550,7 +2562,7 @@ pub(crate) async fn load_file_tree_html(
|
||||
max_results: None,
|
||||
};
|
||||
let result = match load_projection_snapshot(config, context, &spec).await {
|
||||
Ok(snapshot) => Some(snapshot.projection),
|
||||
Ok(snapshot) => Some((snapshot.projection, false)),
|
||||
Err(_) if config.allow_dev_fixtures => {
|
||||
let documents = active_document_id
|
||||
.map(|document_id| {
|
||||
@@ -2577,16 +2589,28 @@ pub(crate) async fn load_file_tree_html(
|
||||
"mindmap_docs": [],
|
||||
"mindmap_asset_children": {}
|
||||
});
|
||||
execute_kernel_query(context, workspace_id, projection_query(&spec), dev_dataset).ok()
|
||||
execute_kernel_query(context, workspace_id, projection_query(&spec), dev_dataset)
|
||||
.ok()
|
||||
.map(|projection| (projection, true))
|
||||
}
|
||||
Err(_) => None,
|
||||
};
|
||||
result.map(|projection| {
|
||||
result.map(|(projection, dev_fixture)| {
|
||||
let rows = collect_filetree_render_rows(&projection, active_document_id);
|
||||
render_initial_filetree_html(&FileTreeInitialRenderInput { rows })
|
||||
let html = render_initial_filetree_html(&FileTreeInitialRenderInput { rows });
|
||||
mark_dev_fixture_html(html, dev_fixture, "file-tree")
|
||||
})
|
||||
}
|
||||
|
||||
fn mark_dev_fixture_html(html: String, dev_fixture: bool, kind: &'static str) -> String {
|
||||
if !dev_fixture {
|
||||
return html;
|
||||
}
|
||||
format!(
|
||||
r#"<span hidden data-mnote-dev-fixture="true" data-mnote-dev-fixture-kind="{kind}"></span>{html}"#
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn render_local_sidebar_tree_html(
|
||||
root_uri: &str,
|
||||
active_document_id: Option<&str>,
|
||||
@@ -2614,8 +2638,9 @@ pub(crate) fn render_local_file_tree_html(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::app::{build_app, AppConfig, AppState};
|
||||
use crate::context::RequestContext;
|
||||
use axum::body::{to_bytes, Body};
|
||||
use axum::http::{header, Request, StatusCode};
|
||||
use axum::http::{header, HeaderMap, Method, Request, StatusCode, Uri};
|
||||
use serde_json::Value;
|
||||
use tower::util::ServiceExt;
|
||||
|
||||
@@ -2701,6 +2726,9 @@ mod tests {
|
||||
.expect("body");
|
||||
let html = String::from_utf8(body.to_vec()).expect("html");
|
||||
assert!(html.contains("data-testid=\"wolai-sidebar\""));
|
||||
assert!(html.contains("data-mnote-dev-fixture-kind=\"workspace-shell\""));
|
||||
assert!(html.contains("data-mnote-dev-fixture-kind=\"sidebar-tree\""));
|
||||
assert!(html.contains("data-mnote-dev-fixture-kind=\"file-tree\""));
|
||||
assert!(html.contains("data-testid=\"wolai-topbar\""));
|
||||
assert!(html.contains("data-testid=\"wolai-floating-ai\""));
|
||||
assert!(html.contains("星标置顶"));
|
||||
@@ -2755,7 +2783,7 @@ mod tests {
|
||||
.headers()
|
||||
.get("x-mnote-page-aggregate-owner")
|
||||
.and_then(|value| value.to_str().ok()),
|
||||
Some("rust-kernel")
|
||||
Some("compat-join")
|
||||
);
|
||||
assert_eq!(
|
||||
response
|
||||
@@ -2770,7 +2798,7 @@ mod tests {
|
||||
let payload: Value = serde_json::from_slice(&body).expect("json");
|
||||
assert_eq!(payload["owner"], "mnote-web");
|
||||
assert_eq!(payload["schema"], "mnote.page_aggregate.v1");
|
||||
assert_eq!(payload["result"]["source"], "KernelProjection");
|
||||
assert_eq!(payload["result"]["source"], "CompatMetaContentJoin");
|
||||
assert_eq!(payload["result"]["projectionVersion"], 1);
|
||||
assert_eq!(payload["result"]["identity"]["documentId"], "doc_1");
|
||||
assert_eq!(payload["result"]["body"]["revision"], 7);
|
||||
@@ -2893,6 +2921,56 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sidebar_and_filetree_do_not_return_dev_fixtures_by_default() {
|
||||
let config = AppConfig {
|
||||
service_name: "mnote-web".into(),
|
||||
service_version: "0.1.0".into(),
|
||||
bind_addr: "127.0.0.1:0".into(),
|
||||
public_bind_addr: "127.0.0.1:3000".into(),
|
||||
legacy_next_base_url: Some("http://127.0.0.1:3100".into()),
|
||||
enable_legacy_next_compat: true,
|
||||
enable_debug_shell_routes: false,
|
||||
hermes_base_path: "/api/hermes".into(),
|
||||
compat_next_base_path: "/api/compat/next".into(),
|
||||
convex_url: None,
|
||||
convex_admin_key: None,
|
||||
allow_dev_fixtures: false,
|
||||
query_fixtures_json: None,
|
||||
mutation_fixtures_json: None,
|
||||
dev_user_id: "dev-user".into(),
|
||||
dev_user_name: "开发用户".into(),
|
||||
dev_user_email: "dev@mnote.local".into(),
|
||||
};
|
||||
let headers = HeaderMap::new();
|
||||
let context = RequestContext::from_http_parts(
|
||||
&Method::GET,
|
||||
&"/documents/doc_1?workspaceId=ws_demo"
|
||||
.parse::<Uri>()
|
||||
.expect("uri"),
|
||||
&headers,
|
||||
);
|
||||
|
||||
let sidebar_html =
|
||||
super::load_sidebar_tree_html(&config, &context, "ws_demo", Some("doc_1")).await;
|
||||
let filetree_html =
|
||||
super::load_file_tree_html(&config, &context, "ws_demo", Some("doc_1")).await;
|
||||
let workspace_projection = super::load_workspace_shell_projection(
|
||||
&config,
|
||||
&context,
|
||||
"ws_demo",
|
||||
Some("doc_1"),
|
||||
"个人空间",
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(sidebar_html.is_none());
|
||||
assert!(filetree_html.is_none());
|
||||
assert!(workspace_projection.degraded);
|
||||
assert!(workspace_projection.my_page_items.is_empty());
|
||||
assert!(!workspace_projection.dev_fixture);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn document_shell_renders_local_markdown_attachment_name_in_html() {
|
||||
let root = std::env::temp_dir().join(format!(
|
||||
|
||||
Reference in New Issue
Block a user